知识前提:Binder 理论部分笔记整理,以及 AIDL 的相关知识。
参考文章:《Android 艺术探索》
1、概述
Android IPC 的途径之一就是使用 Messenger, 它可以在不同进程中传递 Message 对象,在 Message 中放入需要传递的数据,就可以实现在进程间传递。
Messenger 底层基于 AIDL 实现,是一个轻量级的 IPC 方案。
而且 Messenger 只适合传递消息,不能跨进程调用服务端的方法。
2、具体实现
2.1 实现步骤
假设有两个进程 A、B,如果要实现从 A 中发送 messge 到 B 中,则需要先在 B 中创建一个 Handler 对象,并使用这个对象来创建一个 Messenger 对象,然后调用该 Messenger 对象的 getBinder() 方法,将其内部持有 binder 对象的引用传递给进程 A。
// Messenger.java
public Messenger(Handler target) {
// 得到 Handler 内部的 binder 对象的引用
mTarget = target.getIMessenger();
}
// Handler.java
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
// MessengerImpl 时机上是基于 AIDL 的 binder 对象
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);
}
}
进程 A 得到上述 binder 对象的远程引用后,利用它在进程 A 中创建另外一个 Messenger 对象。
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
之后,就利用在 A 进程中的那个 Messenger 对象的 send()
方法发送 Message 到 B 进程中来。实际上是利用在 A 进程中的远程 biner 对象引用来发送 msg。
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
然后,就可以在 B 进程中的 handler.handleMessage()
中处理从 A 进程中发送来的消息。
如果要实现在 B 中返回消息给 A,则在 A 中也要单独维护另外的 Handler 与 Messenger 对象(与之前通过远程 binder 引用创建的 Messenger 不同)。然后在给 B 进程发送 Message 的时候,将维护的 Messenger 对象赋值给 Message#replyTo
成员变量。(因为 Messenger 也实现了 Parcelable,因此可以在进程中传递。)
而 B 进程在处理从 A 发送的消息的时候,就可以用从 Message 中取出从 A 中传递来的 Messenger 对象,利用该对象给 A 回复消息。
2.2 演示代码
可能上面文字说得比较绕,可以看看具体的代码,更加容易理解。
代码来自 《Android 艺术探索》,其中 B 进程就是 Service 所处进程,A 进程为 Activity 所处进程。
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyConstants.MSG_FROM_CLIENT:
// 处理从 A 进程中发送来的 message
Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
// 获得 A 进程发送 msg 时附带的维护在 A 中的 Messenger 对象,
// 该对象是基于序列化与反序列化,在 binder 底层实现传递的,
// 因此其会持有 A 中维护的 Messenger 对象内部的 binder 本地对象的远程引用。
Messenger client = msg.replyTo;
Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply", "嗯,你的消息我已经收到,稍后会回复你。");
relpyMessage.setData(bundle);
try {
// 利用 client 给 A 进程发送信息
client.send(relpyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
// 根据 MessengerHandler 实例化对应的 Messenger,即维护一个 Messenger 对象
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
// 其他进程与该 Service 绑定的时候,就可以获得 B 进程的 Messenger 内部
// 持有的本地 binder 对象的远程引用
return mMessenger.getBinder();
}
}
public class MessengerActivity extends Activity {
private static final String TAG = "MessengerActivity";
private Messenger mService;
// 也想 B 进程一样维护一个 Messenger 对象
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyConstants.MSG_FROM_SERVICE:
Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
}
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// 基于 B 进程的维护的 Messenger 对象的内部的 binder 对象的远程引用实例化
// 对应的 Messenger,该 Messenger 对象与在 A 中维护的 Messenger 对象是两回事
mService = new Messenger(service);
Log.d(TAG, "bind service");
Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello, this is client.");
msg.setData(data);
// 将 A 中维护的 Messenger 对象附属在 msg.replyTo 上
msg.replyTo = mGetReplyMessenger;
try {
// 给 B 进程发送消息
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
需要注意,Service 在 AndroidMenifest.xml
中注册的时候,使用 process:name
设置其处于与 Activity 不同的进程。
3、补充内容
-
因为是使用 Handler 来处理消息,因此一次只能处理一个消息请求,不能支持并发处理的情形。
-
支持一对多串行通信,只要能获取到目标进程维护的对应的 Messenger 内部持有的 binder 对象的引用即可。
-
使用 Messenger 的场景并不局限于 Activity 与 Service 的场景,关键在于能够获取到目标进程维护的对应的 Messenger 内部持有的 binder 对象的引用,通过该 binder 对象的引用,就实现了与目标进程的关联。只不过刚好 Service 在系统层面上实现了该 binder 引用的传递,即可以通过绑定 Service 来实现获取 binder 引用,因此方便了我们的使用,而不用我们再单独实现 binder 引用传递这部分的逻辑,而且传递的逻辑暂时没有头绪。
-
使用 Messenger 是单向通信的,即如果要从 A 往 B 发送消息,则 B 中要维护一个本地的 Messenger 对象,然后在 A 中还要基于进程 B 维护的对应的 Messenger 内部持有的 binder 对象的引用再实例化一个 Messenger 对象,这样,总共有两个 Messenger 对象,然后只能从 A 往 B 发送消息。
而如果还要实现从 B 往 A 发送消息,则在 A 中也要维护一个 Messenger 对象,当然,此时无需再将 A 中维护的 Messenger 内部持有的 binder 对象的引用传递给 B,而是直接将 A 中维护的 Messenger 对象附属到发送到 B 的 Message 上即可。这样,实现从 B 反过来往 A 通信,又多了两个 Messenger 对象。注意,这里是多了两个,A 进程维护的一个,传递到 B 中反序列化的又是一个,别把这个反序列化与 A 维护的给看作同一引用,这里是跨进程的,反序列化出来的是一个新的处于 B 进程的对象。
因此,总共维护了两组,4 个 Messenger 对象。
-
数据通过 Message 进行传输,因此只能传输 Bundle 支持的数据类型。
Message 中能够使用的载体只有 what(int)、arg1(int)、arg2(int)、data(Bundle 类型) 以及 replyTo(Messenger 类型),另一个字段 object 在同一进程中实用,但是在 Android 2.2 之前无法跨进程传输,在 2.2 以后,也只支持 系统提供的且实现了 Parcelable 的对象 才支持跨进程传输,而自定义的实现了 Parcelable 的对象没有支持。
总共 5 个载体,支持的范围即 Bundle 支持的数据类型,且因为 Bundle 支持 Serializable 的(包括自自定义的),因此 Messenger 在通过 data 字段传输数据的时候是支持 Serializable 的(包括自自定义的)。