使用 Messenger 跨进程通信
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
什么是Messenger
Messenger 也是IPC的方案之一是基于消息的跨进程通信。基于消息是什么意思Handler是我们最常用的消息机制所以 Messenger 对于使用者来说就像是使用 Handler。实际上 Messenger 就是 AIDL 的上层封装而已它们的底层实现原理都是基于 Binder 的。
Messenger的使用
服务端
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 原理
在创建 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 跨进程通信
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |