Messenger 也是IPC的方案之一,是基于消息的跨进程通信。基于消息是什么意思?Handler是我们最常用的消息机制,所以 Messenger 对于使用者来说就像是使用 Handler。实际上 Messenger 就是 AIDL 的上层封装而已,它们的底层实现原理都是基于 Binder 的。
import android.app.Service
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.util.Log
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", "我收到了Client的消息")
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
。
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.os.RemoteException
import android.util.Log
import androidx.activity.ComponentActivity
class MainActivity: ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindService()
}
var mMessenger: Messenger? = null
private val mServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
mMessenger = Messenger(service)
Log.i("MainActivity", "onServiceConnected IBinder = $service")
}
override fun onServiceDisconnected(name: ComponentName) {
mMessenger = null
}
}
private fun bindService() {
val intent = Intent()
intent.component = ComponentName("com.example.interprocess", "com.example.service.MessengerService")
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
}
fun send() {
mMessenger?.let { messenger ->
val msg = Message.obtain()
msg.what = MSG_CLIENT
val bundle = Bundle().apply {
putString("name", "张三")
putInt("age", 22)
putString("sex", "男")
putString("height", "175cm")
}
msg.data = bundle
msg.replyTo = mClientMessenger
try {
messenger.send(msg)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
}
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)
}
})
override fun onDestroy() {
super.onDestroy()
unbindService(mServiceConnection)
}
}
和 AIDL 使用唯一的差异是将 onServiceConnected()
的 IBinder
参数来构建一个 Messenger
实例。
最后 Messenger
是通过 send()
方法把 msg
发送出去的,感觉就好像是使用 Handler
发送了消息一样, 真正的数据需要保存到 Bundle
里,这里需要提一点的是如果想给 Bundle
里塞实现了 Parcelable
的对象,会在服务端接受参数时爆出 ClassNotFoundException
, 因为两个 App 的 Bundle
是不同类加载器加载的,所以我们使用的时候还是把基本类型数据塞到 bundle
里,这对于大量的复杂数据并不是一个好方式。
因此基于消息机制的 Messenger 虽然给我们带来了比 AIDL 更便捷的使用和理解,但并不适合管理复杂数据的跨进程通信。
可以看到上面的代码还给msg.replyTo
赋值了一个 mClientMessenger
, 这个就是客户端接受消息的 messenger
,还记得在服务端回调给客户端的操作就是 msg.replyTo.send(replyMsg)
,客户端的 messenger
消息处理也是一样的。
我们在服务端和客户端都打印了一下线程,最后发现接受消息时都已经处于主线程了,这和 messenger
构造函数的 handler
入参有关,handler
指定了主线程的 Looper
,那么接收消息时就是在主线程。
在创建 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 就是异步调用,因此 send()
方法不需要返回值。 还有就是参数 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()
方法即可回传数据。
Messenger 是基于消息机制的跨进程通信,原理就是对 AIDL 的封装。
Messenger 并不太适合管理复杂数据的跨进程传递。
Messenger 天然支持异步的跨进程调用。
无论是客户端还是服务端接收跨进程消息时都处于主线程,免去了切换线程步骤。
注:本文内容转载自 Messenger 跨进程通信