【问题标题】:Android BroadcastReceiver or simple callback method?Android BroadcastReceiver 还是简单的回调方法?
【发布时间】:2012-05-21 11:02:07
【问题描述】:

在我的项目中,我使用BroadcastReceivers 作为来自长时间运行线程的回调(例如,通知活动下载已完成并从工作人员Thread 发送一些响应数据,以便活动可以显示适当的消息给用户..)。 要使用BroadcastReceivers,我每次使用它时都必须小心注册和注销广播接收器,并且当我使用这种方法进行更多不同的操作(如下载、制作WebService 调用等)。为了通过广播的意图发送自定义对象,我还需要制作对象Parcelable

与这种方法不同,我还看到了似乎比我使用的方法更简单的回调方法方法。回调方法是简单的接口方法实现,可用于实现与应用程序消息中的 BroadcastRecaiver 相同的效果。 这种方法不需要 Parcelable 实现来返回复杂的对象,它不使用像BroadcastReceiver 这样的键。我认为不好的部分是我需要在调用回调方法之前检查回调对象的空值.. 并且还要确保我在 UI 线程上运行来自实现的代码,这样我就可以更新 UI 而不会出错。

好的,我希望你明白我的意思:)。

现在的问题是,当仅在单个应用程序内部使用时,您认为回调方法是否比 BroadcastReceiver 方法更好(更轻、更简洁、更快……)? (请注意,我没有使用 Android Service 进行后台工作。只是 AsyncTaskThreads)

谢谢!

【问题讨论】:

    标签: android multithreading callback broadcastreceiver


    【解决方案1】:

    这是一个非常有趣的问题,我也遇到了同样的问题。在我看来,这两种机制都可以完全使用,正确的使用方法取决于您的用例。在决定之前,请考虑以下几点。

    使用回调机制有一些好处,但也有局限性:

    专业版

    • 实施起来简单直接。
    • 您可以在相互交互的组件之间获得类型安全。
    • 您可以返回任意对象。
    • 它简化了测试,因为您只需在单元测试中注入一个模拟回调(例如通过 mockito 或类似的东西生成)。

    对比

    • 您必须切换到主线程才能进行 UI 操作。
    • 您只能建立一对一的关系。如果没有进一步的工作,1 对 n 关系(观察者模式)是无法实现的。在这种情况下,我更喜欢 Android 的 Observer / Observable 机制。
    • 正如您已经说过的,如果回调可能是可选的,则在调用回调函数之前始终必须检查 null
    • 如果您的组件应该提供一种具有不同服务功能的服务API,并且您不希望只有几个通用回调函数的回调接口,您必须决定是否为每个服务函数提供特殊的回调接口或者您是否提供具有大量回调函数的单个回调接口。在后一种情况下,所有用于对 API 的服务调用的回调客户端都必须实现完整的回调接口,尽管大多数方法主体都是空的。您可以通过实现一个带有空主体的存根来解决此问题,并使您的回调客户端从该存根继承,但如果已经从另一个基类继承,这是不可能的。也许您可以使用某种动态代理回调(请参阅http://developer.android.com/reference/java/lang/reflect/Proxy.html),但它会变得非常复杂,我会考虑使用另一种机制。
    • 如果服务调用者不能直接访问回调调用的客户端,则必须通过各种方法/组件进行传播。

    关于BroadcastReceiver-方法的几点:

    专业版

    • 您实现了组件之间的松散耦合。
    • 您可以建立一对一的关系(包括一对一)。
    • onReceive() 方法始终在主线程上执行。
    • 您可以通知整个应用程序中的组件,因此通信组件不必“看到”彼此。

    对比

    • 这是一种非常通用的方法,因此对Intent 传输的数据进行编组和解组是另一个错误来源。
    • 如果您想消除与其他应用的关联,您必须使 Intent 的操作独一无二(例如,通过添加包名称),因为它们的最初目的是在应用之间进行广播。
    • 您必须管理BroadcastReceiver-注册和注销。如果您想以更舒适的方式执行此操作,您可以实现自定义注释以使用应注册的操作来注释您的 Activity,并实现一个基本的 Activityclass,该类在其 @ 中使用 IntentFilters 进行注册和注销987654334@分别。 onPause()methods。
    • 正如您所说,使用Intent 发送的数据必须实现Parcelable 接口,但此外还有严格的大小限制,如果您传输大量数据会导致性能问题你的Intent。请参阅http://code.google.com/p/android/issues/detail?id=5878 进行讨论。因此,如果您想发送图像,例如,您必须将它们临时存储在存储库中,并发送相应的 ID 或 URL 以从您的 Intent 的接收者访问图像,使用后将其从存储库中删除。如果有多个接收者,这会导致进一步的问题(应该何时从存储库中删除图像,谁应该这样做?)。
    • 如果您过度使用这种通知机制,您的应用程序的控制流可能会被隐藏,并且在调试时您最终会绘制带有Intents 序列的图形,以了解触​​发特定错误的原因或此通知链中断的原因在某个时候。

    在我看来,即使是移动应用程序也应该具有至少基于 2 层的架构:UI 层和核心层(具有业务逻辑等)。通常,长时间运行的任务在核心层内的一个自己的线程中执行(可能通过AsyncTaskHandlerThread,如果使用MessageQueues)在核心层内,一旦此任务完成,UI 应该更新。通常,使用回调可以实现组件之间的紧密耦合,因此我更喜欢仅在层内使用这种方法,而不是跨层边界进行通信。对于 UI 层和核心层之间的消息广播,我将使用 BroadcastReceiver- 方法,让您将 UI 层与逻辑层分离。

    【讨论】:

    • 我已将此作为答案授予并给予赏金,因为这是最完整的答案。谢谢大家的想法!
    • 优秀的答案。我是回调/接口模式的粉丝,但我经常继承大量使用广播意图模式的项目,我经常想知道我是否走错了路。很高兴听到这真的是设计偏好而不是最佳实践的问题。我喜欢接口提供的类型安全性和代码清晰性,我现在可能会坚持使用它们
    • 很有趣很详细的意见+1
    • 您可以使用LocalBroadcastManager 将您的广播保持在您的应用本地。这有助于防止出现应用交叉问题,并让您放心数据的去向。
    • 这里没有两个选项。有许多。我永远不会完全排除 EventBus、RxJava Subject 或应用程序级对象。在不同的情况下,每种方法都有其优点和缺点。
    【解决方案2】:

    我看不出在您的情况下使用 BroadcastReceiver 会获得什么。回调或者更好的可能是Handlers 将是这样做的方式。当您不知道订阅者是谁时,BroadcastReceiver 很好。

    【讨论】:

      【解决方案3】:

      我将在您已经收到的其他很好的答案中添加另一个选项...

      您不必创建广播接收器来接收 Intent。在您的 android 清单文件中,您可以注册任何活动以接收意图:

      <activity android:name=".MyActivity">
              <intent-filter >
                    <action android:name="intent.you.want.to.receive" /> 
                    <category android:name="android.intent.category.DEFAULT" /> 
              </intent-filter>     
      ....
      </activity>
      

      然后在您的活动中覆盖onNewIntent(Intent) 方法以接收它。

      要发送 Intent,请使用 Context.startActivity(Intent) 方法。您很可能希望将 FLAG_ACTIVITY_SINGLE_TOP 标志添加到您的 Intent 中,这样它就不会创建您的 Activity 的新实例(如果一个已经在运行)。

      编辑:我刚刚注意到您在单个应用程序中运行。因此,一个简单的回调可能是最好的。上面的解决方案确实适用于单个应用程序,但更适合不同的应用程序。我会把它留在这里以防万一它对某人有帮助。祝你好运!

      【讨论】:

      • 很好的提示!不过,我想补充一点,同样的 &lt;intent-filter&gt; 方法也适用于服务,如果您想隔离非 UI 特定的实现,在这种情况下,您可以调用 Context.startService(Intent)。如果您的目标服务扩展IntentService,您将免费获得一个“异步消息队列”,因为IntentService 当时只会在一个Intent 上执行,而onHandleIntent 方法将在工作线程上执行。
      【解决方案4】:

      如果您需要跨应用程序发送广播,而回调(或 Alex 建议的处理程序)更适合您的情况,则应使用广播接收器。

      如果你想使用这两个以外的,可以考虑使用Observer(android中包含的接口)和delegate。

      代表请考虑this SO post

      【讨论】:

        【解决方案5】:

        不确定目标是什么,但是如果您希望保持使用 intent 和 broadcastReceiver 的相同想法,并且想要比普通的 broadcastReceiver 更好的性能和安全性,您可以试试这个演示,可在 android 支持库中找到:

        http://developer.android.com/resources/samples/Support4Demos/src/com/example/android/supportv4/content/LocalServiceBroadcaster.html

        如果没有,您可以随时使用 asyncTask 、 service 、 handlers 等...

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-02
          • 2014-04-10
          • 2016-08-06
          • 2017-05-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多