Где разместить BroadcastReceiver в Android MVP?
У меня есть реализация BroadcastReceiver, которая принимает события сетевого подключения. его объявлено в AndroidManifest.xml и вызывается Android автоматически при возникновении сетевых событий.
BroadcastReceiver:
public class ConnectivityChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.v(TAG, "action: " + intent.getAction());
Log.v(TAG, "component: " + intent.getComponent());
}
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.test">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
...
<receiver
android:name=".ConnectivityChangeReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
</application>
</manifest>
Я хотел бы использовать архитектуру образца Google MVP, описанную здесь для моего приложения:
https://github.com/googlesamples/android-architecture/tree/todo-mvp/
Используя вышеупомянутую архитектуру, просто интересно:
-
Где должен быть размещен мой BroadcastReceiver?
-
Если моему BroadcastReceiver нужно писать в базу данных, то как лучше всего это сделать?
-
Если моему BroadcastReceiver нужно обновить пользовательский интерфейс, то как лучше всего это сделать?
Ответы
Ответ 1
- Лично я считаю, что события из
BroadcastReceiver
должны передаваться ведущему.
-
Основываясь на инструкции 1, ведущий держит ссылку на Interactor
/Contract
/Use case
, который должен обрабатывать операции db.
BroadcastReceiver
--event → Presenter
→ Interactor
--- > Repository
-
Основываясь на заявлении 1, снова ведущий должен использовать событие и делать вызов для представления.
BroadcastReceiver
--event → Presenter
→ (может быть, некоторые вещи, бизнес-логика) --- > View
Вот что у меня есть, минимальный примерный фрагмент, в котором суммируется то, что я сказал:
private class NetworkBroadcastReceiver23 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//... redacted code.../
boolean connected = activeNetworkInfo != null && activeNetworkInfo.isConnected();
mPresenter.onConnectionChanged(activeNetworkInfo,connected);
}
}
Поместите приемник в действие, потому что оттуда вы передаете события ведущему. Это облегчит тестирование презентатора для изменения соединения. Трудно добиться разделения внимания на события платформы, я хотел оставить свои слои свободными от компонентов и классов android sdk. Другой способ, на который указал Алекс Шутов, - смешивать шаблон MVP и Observer, если вы рассматриваете BroadcastReceiver как внешний объект, а не источник события.
Да, я согласен, что вы можете улучшить метод, избавившись от параметра NetworkInfo
.
Ответ 2
В шаблоне проектирования MVP Модель имеет все сущности для подключения к внешнему миру (например, репозиторий для извлечения данных и локального сохранения данных). Широковещательный приемник - это вход для внешних событий, который, в конечном итоге, изменит модель. Хорошее сравнение - "входной порт" в гексагональной архитектуре.
Presenter определяет способ отображения данных из модели, но вся бизнес-логика, включая реакцию на другую систему или пользовательские события, должна быть внутри модели.
Просмотр и презентатор можно динамически изменять в зависимости от используемой программы режима, скажем, вы хотите использовать другую версию пользовательского интерфейса или более упрощенное поведение пользовательского интерфейса, но вся логика должна оставаться неизменной, включая реакцию на внешние события. Вот почему BroadcastReceiver следует размещать внутри модели.
НИКОГДА не следует размещать приемник Broadcast в Activity, потому что Activity является системным контейнером для (View), по крайней мере, если вы следуете шаблону MVP.
В случае, если ваш проект достаточно сложный, рассмотрите абстрагирование от BroadcastReceiver некоторым интерфейсом ExternalInput, который можно легко высмеять во время тестирования и использовать его внутри модели.
Ответ 3
Если вы упаковываете по функциям, вы можете просто создать пакет receiver
внутри пакета функций и поместить в него класс BroadcastReceiver
. Если у вас нет подпакетов, просто поместите класс BroadcastReceiver
в свой пакет функций.
Ответ 4
Ваша трансляция должна быть View. Затем он вызывает метод Presenter, который изменяет состояние некоторого NetworkStateService
(который является уровнем модели). Когда состояние NetworkStateService
изменено, оно уведомляет Ведущие, что сеть доступна, и они могут делать запросы. И эти докладчики должны обновить интерфейс. Все эти докладчики должны быть в качестве слушателей в NetworkStateService.
Для длительных операций, таких как работа с db или сетью, вы должны запустить Service
. Причина этого в том, что трансляция будет убита через 10 секунд после получения. Вы должны поставить Presenter в этот Service
и работать с Model из этого презентатора.
Ответ 5
Если вам необходимо выполнить операции с базой данных/обновить интерфейс, тогда вы должны поместить BroadcastReceiver в соответствующую активность.
Вот пример кода.
public class YourActivity extends AppCompatActivity {
@Override
protected void onResume() {
super.onResume();
registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
// public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mConnectivityReceiver);
}
public BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
checkIntent(intent);
}
private void checkIntent(Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
NetworkInfo networkInfo = (NetworkInfo) bundle.get("networkInfo");
doYourStuff(networkInfo.isConnected());
}
}
};
private void doYourStuff(boolean isNetworkConnected) {
//Update your UI here
//Do database Operations here
}
}
Сделайте свои вещи в doYourStuff() на основе сетевых подключений.
С помощью этого подхода вам не нужно регистрировать свой BroadcastReceiver в файле Menifest.xml.