1. 概念
Messenger,即進程間通信的信使.它是基于Message的進程間通信,我們可以像在線程間利用Handler.send(Message)一樣.
Messenger是一種輕量級的IPC方案,它的底層實現(xiàn)其實就是AIDL.跨進程通信使用Messenger時,Messenger會將所有服務調用加入隊列,然后服務端那邊一次處理一個調用,不會存在同時調用的情況.而AIDL則可能是多個調用同時執(zhí)行,必須處理多線程問題.
對于大多數(shù)應用,跨進程通信無需一對多,也就是無需執(zhí)行多線程處理,此時使用Messenger更適合.
2. 使用
2.1 大致流程
- 服務端實現(xiàn)一個Handler,由其接收來自客戶端的每個調用的回調
- 服務端使用Handler來創(chuàng)建Messenger對象
- Messenger創(chuàng)建一個IBinder,服務端通過onBind()將其返回給客戶端
- 客戶端使用IBinder將Messenger實例化,然后再用起將Message對象發(fā)送給服務端
- 服務端在其Handler#handleMessage()中,接收每個Message
2.2 案例
2.2.1 服務端
首先需要在服務端創(chuàng)建一個Handler用于接收消息,然后將此Handler傳遞給Messenger,并在onBind中將該Messenger的底層binder返回回去.
//這里服務端Service是運行在單獨的進程中的 android:process=":other"
class MessengerService : Service() {
private lateinit var mMessenger: Messenger
override fun onBind(intent: Intent): IBinder {
log(TAG, "onBind~")
//傳入Handler實例化Messenger
mMessenger = Messenger(IncomingHandler(this))
//將Messenger中的binder返回給客戶端,讓它可以遠程調用
return mMessenger.binder
}
//處理客戶端傳遞過來的消息(Message) 并根據(jù)what決定下一步操作
internal class IncomingHandler(
context: Context,
private val applicationContext: Context = context.applicationContext
) : Handler(
Looper.getMainLooper()
) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_SAY_HELLO -> {
Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
log(TAG, "hello!")
}
else -> super.handleMessage(msg)
}
}
}
}
2.2.2 客戶端
客戶端進程中,首先是需要綁定遠程Service.綁定完成之后,在onServiceConnected()
中拿到遠程Service返回的IBinder對象,用此IBinder對象實例化客戶端這邊的Messenger.有了這個Messenger,就可以通過這個Messenger往服務端發(fā)送消息了.示例代碼如下:
class MessengerActivity : TitleBarActivity() {
/** 與服務端進行溝通的Messenger */
private var mService: Messenger? = null
/** 是否已bindService */
private var bound: Boolean = false
private val mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mService = Messenger(service)
bound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
mService = null
bound = false
}
}
override fun getThisTitle(): CharSequence {
return "Messenger"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_messenger)
btnConnect.setOnClickListener {
connectService()
}
btnSayHello.setOnClickListener {
sayHello()
}
}
private fun sayHello() {
if (!bound) {
return
}
//創(chuàng)建,并且發(fā)送一個message給服務端 Message中what指定為MSG_SAY_HELLO
val message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
try {
mService?.send(message)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
private fun connectService() {
Intent().apply {
action = "com.xfhy.messenger.Server.Action"
setPackage("com.xfhy.allinone")
}.also { intent ->
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
if (bound) {
unbindService(mServiceConnection)
bound = false
}
}
}
通過示例代碼我們知道客戶端通過Messenger與服務端進行通信時,必須將數(shù)據(jù)放入Message中,Messenger和Message都實現(xiàn)了Parcelable接口,因此是可以跨進程傳輸?shù)?Message只能通過what、arg1、arg2眉睹、Bundle以及replyTo來承載需要傳遞的數(shù)據(jù),如果需要傳遞Serializable或者Parcelable的對象則可以放進Bundle里面進行傳遞,Bundle還支持其他大量的數(shù)據(jù)類型.
2.2.3 服務端向客戶端發(fā)送消息
有時候我們需要客戶端能響應服務端發(fā)送的消息,此時我們只需要在上面的示例的基礎上簡單修改即可.
服務端這邊每次收到消息,都回復一條消息給客戶端,方便測試
internal class IncomingHandler : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_SAY_HELLO -> {
log(TAG, "hello!")
//客戶端的Messenger就是放在Message的replyTo中的
replyToClient(msg, "I have received your message and will reply to you later")
}
MSG_TRANSFER_SERIALIZABLE -> log(TAG, "傳遞過來的對象: ${msg.data?.get("person")}")
else -> super.handleMessage(msg)
}
}
private fun replyToClient(msg: Message, replyText: String) {
val clientMessenger = msg.replyTo
val replyMessage = Message.obtain(null, MSG_FROM_SERVICE)
replyMessage.data = Bundle().apply {
putString("reply", replyText)
}
try {
clientMessenger?.send(replyMessage)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
}
而客戶端這邊需要做出響應,則還需在客戶端創(chuàng)建一個Messenger,并為其創(chuàng)建一個Handler用于接收服務端傳遞過來的消息.在客戶端發(fā)送消息時,需要將Message#replyTo
設置為客戶端的Messenger. 服務端拿到這個Messanger才能回復消息.
/** 客戶端這邊的Messenger */
private var mClientMessenger = Messenger(IncomingHandler())
class IncomingHandler : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_FROM_SERVICE -> {
log(TAG, "Received from service: ${msg.data?.getString("reply")}")
}
else -> super.handleMessage(msg)
}
}
}
private fun sayHello() {
if (!bound) {
return
}
//創(chuàng)建,并且發(fā)送一個message給服務端 Message中what指定為MSG_SAY_HELLO
val message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
//注意 這里是新增的
message.replyTo = mClientMessenger
message.data = Bundle().apply {
putSerializable("person", SerializablePerson("張三"))
}
try {
mService?.send(message)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
服務端調用sayHello()
之后,輸出日志如下:
2020-12-31 11:59:40.420 29702-29702/com.xfhy.allinone D/xfhy_messenger: hello!
2020-12-31 11:59:40.421 29649-29649/com.xfhy.allinone D/xfhy_messenger: Received from service: I have received your message and will reply to you later
日志里面明顯看到是2個進程,所以現(xiàn)在是達到是雙向通信的目的.Messenger的使用大概就是這些了,下面是Messenger的大致工作原理圖
//todo xfhy 插圖 Messenger的工作原理 Android開發(fā)藝術探索(P93)
3. 原理
3.1 客戶端->服務端通信
服務端
當客戶端到服務端單向通信時,我們來看一下大致的原理.首先是服務端這邊在onBind方法中返回了Messenger的binder對象
override fun onBind(intent: Intent): IBinder {
//傳入Handler實例化Messenger
mMessenger = Messenger(IncomingHandler())
//將Messenger中的binder返回給客戶端,讓它可以遠程調用
return mMessenger.binder
}
我們看下Messenger里面的binder是什么:
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
public IBinder getBinder() {
return mTarget.asBinder();
}
從Messenger的構造方法(IMessenger.Stub.asInterface()
)可以看出它底層應該是使用的AIDL搞的.getBinder()其實是將調用了mTarget.asBinder()
,而mTarget是我們傳進來的Handler里面拿出來的,跟進Handler.getIMessenger()
看一下:
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);
}
}
原來IMessenger是Handler的內部類MessengerImpl,它只有一個send方法.結合上面Messenger的源碼,我們發(fā)現(xiàn)調用Messenger的send方法其實就是調用這里的MessengerImpl的send方法,然后這個send里面將Message轉發(fā)給Handler#sendMessage()
,最后也就是去了Handler#handleMessage()
里面接收到這個Message.
MessengerImpl是繼承自IMessenger.Stub
,這一看就感覺是AIDL文件自動生成的嘛,easy.大膽猜測一下對應的aidl文件應該是IMessenger.aidl
,我們去源碼里面找IMessenger.aidl
,果然在frameworks/base/core/java/android/os/IMessenger.aidl
這個位置找到了它.內容如下:
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}
根據(jù)aidl文件,它自動生成的IMessenger.java
應該長下面這樣:
package android.os;
public interface IMessenger extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements IMessenger {
private static final java.lang.String DESCRIPTOR = "android.os.IMessenger";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an android.os.IMessenger interface,
* generating a proxy if needed.
*/
public static IMessenger asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IMessenger))) {
return ((IMessenger) iin);
}
return new IMessenger.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_send: {
data.enforceInterface(descriptor);
android.os.Message _arg0;
if ((0 != data.readInt())) {
_arg0 = android.os.Message.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.send(_arg0);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements IMessenger {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void send(android.os.Message msg) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((msg != null)) {
_data.writeInt(1);
msg.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_send, _data, null, android.os.IBinder.FLAG_ONEWAY);
} finally {
_data.recycle();
}
}
}
static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void send(android.os.Message msg) throws android.os.RemoteException;
}
這就好辦了,這就明擺著說明Messenger底層是基于AIDL實現(xiàn)的.服務端這邊這條線: Service#onBind()->mMessenger.getBinder()->Handler#getIMessenger()->MessengerImpl(IMessenger.Stub)
,其實就是和我們使用AIDL一樣將IXXX.Stub的子類通過onBind返回回去,客戶端綁定的時候好拿到binder對象.接收客戶端的消息時,是通過MessengerImpl轉發(fā)給Handler來完成的,服務端這邊定義的那個Handler就可以在handleMessage()
中處理跨進程傳遞過來的Message,從而理解客戶端想要調用什么服務,然后執(zhí)行相應的邏輯.
客戶端
再看客戶端這邊,在onServiceConnected()
時,將服務端返回的IBinder對象放進Messenger里.
//MessengerActivity.kt
private val mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mService = Messenger(service)
bound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
mService = null
bound = false
}
}
//Messenger.java
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
public Messenger(IBinder target) {
//這里asInterface 出來的其實就是 IMessenger.Stub.Proxy對象
mTarget = IMessenger.Stub.asInterface(target);
}
IBinder對象放進Messenger原來就是熟悉的操作IMessenger.Stub.asInterface()
,簡單.然后客戶端這邊給服務端發(fā)消息的時候通過構建出來的Messenger調用send方法發(fā)送,而Messenger內部send的實現(xiàn)其實就是調用IMessenger.Stub.Proxy
(跨進程了)的send方法.調用之后,服務端那邊在Handler的handleMessage里收到這條消息(Message),從而實現(xiàn)了跨進程通信.
3.2 服務端->客戶端通信
客戶端與服務端的通信與我們用AIDL的方式實現(xiàn)幾乎一致,完全可以我們自己實現(xiàn),Messenger只是幫我們封裝好了而已.下面來看一下服務端與客戶端的通信.
服務端需要與客戶端通信的話,需要客戶端在send消息的時候將客戶端Messenger存放在消息的replyTo中.
private fun sayHello() {
val message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
//將客戶方的Messenger放replyTo里
message.replyTo = mClientMessenger
mService?.send(message)
}
將消息發(fā)送到服務端時,因為是跨進程,所以肯定需要用到序列化與反序列化Message.看下Message的反序列化代碼:
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
workSourceUid = source.readInt();
}
主要是看一下replyTo是怎么反序列化的,它調用了Messenger的readMessengerOrNullFromParcel方法:
public static void writeMessengerOrNullToParcel(Messenger messenger,
Parcel out) {
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
: null);
}
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
return b != null ? new Messenger(b) : null;
}
writeMessengerOrNullToParcel中將客戶端的messenger.mTarget.asBinder()
進行了寫入,然后在readMessengerOrNullFromParcel時進行了恢復,而messenger.mTarget
就是上面分析的MessengerImpl,asBinder()
是其父類IMessenger.Stub
里面的一個方法:
@Override
public android.os.IBinder asBinder() {
return this;
}
就是將自身返回出去.也就是說,服務端反序列化出來的replyTo對應Messenger中的IBinder其實就是客戶端的MessengerImpl對象.于是服務端拿到這個Messenger就可以發(fā)送消息,通過這個IBinder對象跨進程通信,客戶端就接收到消息了.
4. 小結
跨進程通信時,Messenger比AIDL更常用(滿足使用條件的時候),因為用起來比較方便,而且官方也更推薦.在使用Messenger的同時,我們需要了解其原理:
- 客戶端與服務端單向通信時,利用的是AIDL接口的原理,和我們平時寫的方式一樣
- 服務端與客戶端通信時,利用客戶端發(fā)送消息時Message對象需要序列化與反序列化,將客戶端的binder對象封裝在里面的replyTo字段中,服務端那邊反序列化時再將其取出組裝成Messenger.有了這個客戶端的binder對象,當然也就能夠與客戶端進行跨進程通信了.