Android中的Handler到Handler通信与Messenger到Messenger通信的区别

4

问题:

在Android中,使用Handler到处理程序的通信是否比使用Messenger到信使的通信更好(更快、开销更小)?

情况:

一个Android应用程序有许多活动和一个正在运行的服务(启动服务)。在服务内,除了服务的主线程外,还有一些线程在运行。 应用程序启动,第一个活动启动服务,服务启动,第一个活动转到第二个活动,第二个活动绑定到服务,...

从Handler到Handler:

...服务提供对服务处理程序的引用,第二个活动定义自己的处理程序,第二个活动现在可以使用服务处理程序直接与服务进行通信。要让服务处理程序知道它必须回复到一个活动处理程序,可以使用Message对象中的 msg.obj 字段设置“回复”处理程序(即活动内的处理程序)。现在成功建立了活动和服务之间的双向通信。

从信使到信使:

...服务分配了对服务信使的引用,第二个活动定义了自己的信使,第二个活动现在可以间接地使用服务信使与服务进行通信。服务信使将消息(Message对象)转换为Parcelable对象,然后重新将Parcelable对象转换回新的(但相等的)Message对象,并将其交给服务的处理程序。要让服务信使知道它必须回复活动信使,可以使用活动中的信使设置msg.replyTo字段。双向通信成功建立。

“问题”:

为什么需要“信使到信使”的设置,而我只需要在一个进程的边界内进行直接通信呢?在一个进程内部,所有线程都可以通过使用它们各自正确设置的处理程序轻松地进行通信。我不希望信使首先扁平化在两个处理程序之间共享的Message对象,这只会增加开销,除了“盲目遵循Android服务教程”外没有任何目的。

可能的解决方案:

启动服务,绑定一次,让服务提供一个本地绑定器对象,在ServiceConnection实现的onServiceConnected()中设置活动中的服务处理程序,让活动在全局内存空间中存储它,切换到第三、第四、第五个活动,您永远不必在所有其他活动中再次绑定,因为所有活动都知道它们自己的处理程序(在每个活动中单独设置),并且可以从全局内存空间获取服务处理程序。如果服务需要响应第四个活动的处理程序,则第四个活动只需将其自己的处理程序(fourthHanlder)添加到msg.obj字段中,服务就知道回复消息必须发送到哪里了。就是这样。
此外:活动运行在UI线程/主线程上,服务也在UI线程/主线程上运行,因此实际上它们是同一个线程的一部分,只是不同的处理程序。或者这是我错误的想法吗?
这意味着整个 Messenger 只是同一进程中线程之间本地(内部)通信的额外开销!除非 Android 系统在内部确定 Messengers 是否相互通信在同一个进程中,并绕过消息的扁平化和跳过将其转换为 Parcelable 对象的翻译,以便 Messengers 实际上在处理程序之间直接通信(用户/程序员实际上并不知道)。我不知道这是否正确。

我发现只有三种方法可以使用:

  • Intent(不能使用 Intent 发送对象!)
  • Messenger(与直接使用 Handlers 相比,可能性受到限制,例如:无法将消息排队到队列的前面)
  • Handlers(仅当处理程序所属的线程位于同一进程中时才可能,否则您需要使用 Messengers)

我相信 Handlers 是在线程之间通信的最快方法,其次是 Messengers,最后是 Intents。这是真的吗?

请分享您对此问题的见解和经验,即使我看错了 :) 谢谢。

1个回答

9
我看到只有三种方法可以在活动和服务之间创建异步通信。
胡说八道。
现在,我会使用事件总线进行服务->活动通信(LocalBroadcastManager、Square的Otto,greenrobot的EventBus)。不需要绑定,不需要自己的处理程序,不需要自己的信使,更加灵活。
除此之外,如果你正在使用绑定,只需创建自己的监听器接口,与您使用OnClickListener监听按钮单击没有任何区别。唯一的变化是您将引发事件(调用侦听器上的方法)以及接收事件。
还有ResultReceiver,但我没有看到它被广泛使用。

太好了!你的一次回复解决了我所有的疑虑。我应该把花送到哪个地址呢?我在greenrobot的EventBus网站的比较表中看到,Greenrobot更快且“更好”(没有注释因为慢吗?)。您是否也会在服务内使用Eventbus进行线程间通信? - user504342
@user504342:“我在greenrobot的EventBus网站的比较表中看到,Greenrobot更快、更好(没有注释是因为慢吗?)”——在微基准测试级别上,Greenrobot的EventBus可能更快。然而,在更合理的事件速率下,这不太可能产生实质性的差异,所以我建议您选择您更喜欢的API。 “您是否也会将Eventbus用于服务内的线程间通信?”——在5年以上的Android开发中,我从未需要在服务中使用多个线程,因此我没有考虑过这个问题。 - CommonsWare
1
我以前从未需要在服务中使用多个线程。但是,因为我同时控制多个蓝牙设备(无法仅在一个线程中完成),所以我确实需要多个线程。GreenRobot EventBus似乎是最佳选择,因为它可以在不同的线程中传递事件,而Otto目前无法做到这一点。你回答中的“无需绑定,无需自己的处理程序/信使”部分有一件事我不明白。当服务启动时,当前活动的活动如何直接联系服务或其线程之一? - user504342
@user504342: "因为我同时控制多个蓝牙设备(无法在一个线程中完成)" -- 这听起来很合理。"GreenRobot EventBus 看起来是最好的选择,因为它可以在不同的线程中传递事件" -- 正确,greenrobot 的 EventBus 提供了更多的线程灵活性。"当服务启动时,当前活动的活动如何直接联系服务或其线程之一?" -- 好吧,你可以使用事件总线本身。或者,通过 startService() 发送命令。绑定是一种选择,但不是唯一的选择。 - CommonsWare
我更喜欢在第一个(主)活动中启动服务,然后从那里开始。然后,当我需要服务通信时,我在该活动中绑定到服务。获取服务处理程序并开始通信(这是我到目前为止的做法)。糟糕的部分是:每次纵向/横向切换发生时都会进行(解)绑定。使用处理程序(我将其保留在全局空间中)可以克服该问题,但现在我认为:那也很糟糕;)问题在于一些活动直接与线程A通信,而其他活动直接与服务中的线程B通信。我该怎么办?... - user504342
不直接使用处理程序(handler2handler很容易:在绑定时从服务中获取主处理程序,请求主线程处理程序向线程A或线程B发送消息,并让它们响应我传递的消息到主线程处理程序(在msg.obj字段中)的活动处理程序。我不知道EventBus的存在,所以这是我想出来的...有点复杂。我认为使用LocalBroadcastMgr或普通事件不适合这种工作,Messengers具有不必要的开销,而Intents只会浪费对象(Messages是可重用的对象)。有什么建议吗? - user504342

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接