什么是Messenger
Messenger 也是IPC的方案之一,是基于消息的跨进程通信。
**基于消息是什么意思?Handler是我们最常用的消息机制,所以 Messenger 对于使用者来说就像是使用 Handler。实际上 Messenger 就是 AIDL 的上层封装而已, 关于 AIDL 可以阅读我另一篇 AIDL介绍。**Android 里的跨进程其实讲来讲去都是基于 Binder 的,无非就是为了某些场景而做了一些封装,使得使用起来更加方便。
使用Messenger
服务端
const val MSG_CLIENT = 0x110
const val MSG_SERVER = 0x111
class MessengerService: Service() {
private val mMessenger = Messenger(object : Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
Log.d("MessengerService", "currentThread ->" + Thread.currentThread().name)
when(msg.what) {
MSG_CLIENT -> {
// 除非就是一个简单的整型,可以把值放到 arg 等属性上,否则最好使用 bundle
val bundle = msg.data
Log.d("MessengerService", "name=" + bundle.get("name") + " ;age=" + bundle.get("age") + " ;height="+bundle.get("height"))
// 服务端封装msg
val replyMsg = Message.obtain()
replyMsg.what = MSG_SERVER
val bundleToC = Bundle()
bundleToC.putString("msg", "谢谢您,我收到了。")
replyMsg.data = bundleToC
// 服务端发送给客户端,这里的replyTo是客户端的messenger实例
msg.replyTo.send(replyMsg)
}
}
super.handleMessage(msg)
}
});
override fun onBind(intent: Intent?): IBinder? {
return mMessenger.binder
}
}
Messenger 本身就是 AIDL 的封装,因此还是通过 service 在 onBind 方法里返回 mMessenger.binder
。然后它就像是使用 Handler 一样在 handleMessage
方法内接收 msg。同样的在 AndroidManifest.xml 文件中声明该 service。
<service android:name=".MessengerService"
android:exported="true">
<intent-filter>
<action android:name="com.example.messengerservice"></action>
</intent-filter>
</service>
客户端
在客户端第一步还是要绑定 service。
private val connection = object: ServiceConnection{
override fun onServiceDisconnected(className: ComponentName?) {
mServiceMessenger = null
}
override fun onServiceConnected(className: ComponentName?, service: IBinder?) {
mServiceMessenger = Messenger(service)
}
}
// 绑定
val intent = Intent();
intent.component = ComponentName("com.example.interprocess", "com.example.service.MessengerService")
bindService(intent, connection, Context.BIND_AUTO_CREATE);
和 aidl 使用唯一的差异是将 onServiceConnected
的 IBinder
参数来构建一个 Messenger 实例。
最后 Messenger 是通过 send
方法把 msg
发送出去的:
val msg = Message.obtain()
msg.what = MSG_CLIENT
val bundle = Bundle()
bundle.putString("name", name)
bundle.putInt("age", age)
bundle.putString("sex", sex)
bundle.putString("height", height)
msg.data = bundle
msg.replyTo = mClientMessenger
mServiceMessenger?.send(msg)
感觉就好像是使用 Handler 发送了消息一样, 真正的数据需要保存到 Bundle 里,这里需要提一点的是如果想给 Bundle 里塞实现了 Parcelable 的对象,会在服务端接受参数时爆出 ClassNotFoundException
, 因为两个 App 的 Bundle 是不同类加载器加载的,所以我们使用的时候还是把基本类型数据塞到 bundle 里,这对于大量的复杂数据并不是一个好方式。
因此个人认为基于消息机制的 Messenger 给我们带来了比 AIDL 更便捷的使用和理解但并不适合管理复杂数据的跨进程通信。
可以看到上面的代码还有一个 mClientMessenger
赋值给了 msg.replyTo
, 这个就是客户端接受消息的 messenger,还记得在服务端回调给客户端的操作就是 msg.replyTo.send(replyMsg)
, 一切都串联了起来,客户端的 messenger 消息处理也是一样的:
private val mClientMessenger = Messenger(object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when(msg.what){
MSG_SERVER -> {
Log.d("MainActivity", "currentThread ->" + Thread.currentThread().name)
Log.d("MainActivity", "server callback = " + msg.data.getString("msg"))
}
}
super.handleMessage(msg)
}
})
我在服务端和客户端都打印了一下线程,最后发现接受消息时都已经处于主线程了,这和 messenger 构造函数的 handler 入参有关,handler 指定了主线程的 Looper,那么接收消息时就是在主线程。
Messenger 原理
前面已经提到过 Messenger 是对 AIDL 的封装,接下来就看看其真面目。
在创建 Messenger 时总是会传入一个 Handler 实例:
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
再进入 handler 看下
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
handler 里的 MessengerImpl 继承了 IMessenger.Stub
,看下 IMessenger:
// IMessenger.aidl
oneway interface IMessenger {
void send(in Message msg);
}
这完全就是一个标准的 aidl 接口,只是系统帮我们做了这一层的封装,然后通过 handler 转化为我们熟悉的消息机制。**值得注意这里有 oneway
关键字,说明了 messenger 就是异步调用。**还有就是参数 Message,既然它能跨进程传递,那么它一定是实现了 Parcelable 接口了,且声明了 aidl 接口:
// 包路径:/frameworks/base/core/java/android/os/Message.aidl
package android.os;
parcelable Message;
// 包路径:/frameworks/base/core/java/android/os/Message.java
public final class Message implements Parcelable {
//...
}
还有一个点,Messenger 实现相互通信的方式是设置一个 msg.replyTo
这个变量也是一个 Messenger, 还记得 AIDL 介绍过支持 aidl 接口的传递,所以客户端的 mClientMessenger
能够传递给服务端,然后调用 send
方法即可回传数据。具体的原理还是得看 Binder 原理。
总结
- Messenger 是基于消息机制的跨进程通信,原理就是对 AIDL 的封装。
- Messenger 并不太适合管理复杂数据的跨进程传递。
- Messenger 天然支持异步的跨进程调用。
- 无论是客户端还是服务端接收跨进程消息时都处于主线程,免去了切换线程步骤。