【问题标题】:Where to put BroadcastReceiver in Android MVP?在 Android MVP 中将 BroadcastReceiver 放在哪里?
【发布时间】:2017-02-01 05:01:12
【问题描述】:

我有一个接收网络连接事件的 BroadcastReceiver 实现。它在 AndroidManifest.xml 中声明,当网络事件发生时由 Android 自动调用。

广播接收者:

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/

使用上面的架构,只是想知道:

  1. 我的 BroadcastReceiver 应该放在哪里?

  2. 如果我的 BroadcastReceiver 需要写入数据库,最好的方法是什么?

  3. 如果我的 BroadcastReceiver 需要更新 UI,最好的方法是什么?

【问题讨论】:

  • 使用 EventBus 怎么样? github.com/greenrobot/EventBus
  • @Rory 一个意图服务怎么样,它需要在您的应用程序流程中显式启动和停止?
  • @ChintanSoni 大泥巴怎么办?! ;)) 他试图通过添加“组件”来创建一些架构,而不是快速和肮脏地解决它,该组件将与每个人交谈并倾听每个人,而无需任何义务或合同。
  • @Ewoks 希望改变观念对您有所帮助;)
  • 只是想知道 EventBus 属于 MVP(或其他架构)的哪个部分......考虑它改变了我的观点

标签: android mvp


【解决方案1】:

您的广播应该是查看。然后,它调用Presenter 方法,改变一些NetworkStateService(这是Model 级别)的状态。当NetworkStateService 的状态发生变化时,它会通知Presenters,该网络可用并且他们可以提出请求。这些 Presenters 应该更新 UI。 所有这些 Presenter 都应该是 NetworkStateService 中的监听器。

对于长时间操作,例如使用 db 或 network,您应该启动 Service。这样做的原因是广播将在接收后 10 秒后被终止。您应该将 Presenter 放入此 Service 并使用此 Presenter 中的 Model

【讨论】:

  • 你的广播应该是 View - 我不知道在哪个宇宙中这是真的,但在这个宇宙中不是
  • @TimCastelijns 你能解释一下你的宇宙吗?)在我的宇宙中,所有输入都可以放在视图层中,因为它是输入。你也可以把它放在域层。这取决于输入类型。如果用户关闭网络,则用户输入 %)
【解决方案2】:

如果您是按功能打包,您可以在功能包内创建receiver 包并将BroadcastReceiver 类放入其中。如果您没有子包,只需将BroadcastReceiver 类放在您的功能包中即可。

【讨论】:

    【解决方案3】:

    如果您需要进行数据库操作/更新 UI,那么您应该将 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() 中做你的事情。

    通过这种方法,您无需在 Menifest.xml 文件中注册您的 BroadcastReceiver。

    【讨论】:

    • 我相信在代码和活动中注册 BroadcastReceiver 意味着我的应用在不运行时不会收到广播,这是我想要的。
    • 是的,它应用程序不会接收广播,因为调用 unregisterReceiver 将: 1:取消注册以前注册的 BroadcastReceiver。 2:已经为此广播接收器注册的过滤器将被删除。 ——
    • 如果这有助于您将此答案标记为最适合其他人。
    • Activity 是 Android 中的纯 UI 系统容器,这就是为什么如果广播接收器修改模型状态,则永远不要在其中放置广播接收器。
    • 这取决于要求。如果您的应用想在应用处于前台时使用 BroadcastReceiver,那么您应该按照此处的要求使用此方法。 stackoverflow.com/a/7636675/5124050
    【解决方案4】:

    在 MVP 设计模式中,模型具有与外部世界连接的所有实体(例如,用于获取数据并将其保存在本地的存储库)。广播接收器是外部事件的输入,最终会修改模型。很好的比较是六边形架构中的“输入端口”。

    Presenter 定义了从模型中显示数据的方式,但所有业务逻辑,包括对另一个系统或用户事件的反应都应该在模型内部。

    View 和 Presenter 可以根据运行的模式程序动态更改,例如,您想使用另一个版本的 UI,或更简单的 UI 行为,但所有逻辑必须保持不变,包括对外部事件的反应。这就是为什么 BroadcastReceiver 应该放在模型里面。

    您永远不应该将广播接收器放入 Activity,因为 Activity 是(视图)的系统容器,至少如果您遵循 MVP 模式。 如果您的项目非常复杂,请考虑通过一些“ExternalInput”接口从 BroadcastReceiver 中抽象出来,这样可以在测试期间轻松模拟并在模型中使用它。

    【讨论】:

    • 你的两个答案都应该是俱乐部,而不是发布不同
    【解决方案5】:
    1. 就我个人而言,我认为来自BroadcastReceiver 的事件应该传递给演示者。
    2. 基于语句 1,presenter 持有对应该处理 db 操作的 Interactor/Contract/Use case 的引用。

      BroadcastReceiver --事件--> Presenter --> Interactor ---> Repository

    3. 基于语句 1,演示者应该再次消费事件并调用视图。

      BroadcastReceiver --事件--> Presenter --> (也许做一些事情,业务逻辑) ---> View p>

    这是我所拥有的,一个简单的例子 sn-p 总结了我所说的:

      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 组件和类的影响。 Alex Shutov 指出的另一种方法是混合 MVP 和 Observer 模式,如果您将 BroadcastReceiver 视为外部实体,而不是事件源。

    是的,我同意您可以通过去掉 NetworkInfo 参数来改进该方法。

    【讨论】:

      猜你喜欢
      • 2010-10-06
      • 1970-01-01
      • 2018-02-08
      • 1970-01-01
      • 2018-04-11
      • 2019-04-03
      • 2012-11-14
      • 2011-08-29
      • 1970-01-01
      相关资源
      最近更新 更多