Messenger 跨进程通信

911 阅读4分钟

什么是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 使用唯一的差异是将 onServiceConnectedIBinder 参数来构建一个 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 天然支持异步的跨进程调用。
  • 无论是客户端还是服务端接收跨进程消息时都处于主线程,免去了切换线程步骤。