Messenger 是 Android 中的一种轻量级的 IPC 方案。它可以翻译为信使,是 Android 中的一个类,通过它可以在不同进程间传递 Message 对象,在 Message 中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。下面看一下 Messenger 类的源码:
/*
* 可以看到 Messenger 实现了 Parcelable 接口,所以它可以在进程间进行传递。
*/
public final class Messenger implements Parcelable {
private final IMessenger mTarget;
/**
* Messenger 的构造方法,创建一个与目标 Handler 关联的 Messenger 对象。
*/
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
/**
* 发送一个 Message 对象给这个 Messenger 的 Handler,然后由该 Handler 来处理消息。
*/
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
/**
* 返回与该 Messenger 相关的 IBinder 对象。
*/
public IBinder getBinder() {
return mTarget.asBinder();
}
/**
* 通过参数中的 IBinder 对象创建一个与之相关联的 Messenger 对象。
*/
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
}
通过源码中的 IMessenger 对象以及 IMessenger.asBinder()、IMessenger.Stub.asInterface() 方法可以看到,其实 Messenger 底层实现就是 AIDL,它只是对 AIDL 进行了封装,使得我们使用起来非常简单。同时,由于它一次只处理一个请求,所以服务端不存在并发执行的情形,因此在服务端我们不用考虑线程同步的问题。
下面看一下它的使用:
eg1:客户端向服务端发送两个 int 型整数,并请求服务端计算两个整型数的结果,但是服务端不返回结果给客户端,只执行计算逻辑。
远程服务端 MessengerService.java:
package com.cfm.messengertest;
/**
* 步骤1. 在服务端创建一个 Service 来处理客户端的连接请求
*/
public class MessengerService extends Service {
private static final String TAG = "cfmtest";
/**
* 步骤2. 创建一个执行客户端的请求的 Handler 类
*/
private static class MessengerHandler extends Handler {
// 执行客户端请求的回调方法
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ClientActivity.MSG_FROM_CLIENT:
Log.d(TAG, "接收到了客户端发起的请求!");
int result = msg.getData().getInt("a") + msg.getData().getInt("b");
Log.d(TAG, "服务端计算两个数相加的结果为:" + result);
break;
default:
super.handleMessage(msg);
}
}
}
/**
* 步骤3: 通过步骤 2 创建一个 Handler 对象,然后创建一个与之相关联的 Messenger
*/
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "远程服务绑定成功!");
/**
* 步骤4: 获取该 Messenger 对象的底层 Binder,然后通过 Binder 完成进程间通信。
*/
return mMessenger.getBinder();
}
}
客户端 ClientActivity.java:
package com.cfm.messengertest;
/**
* 客户端需要绑定远程服务,然后根据服务端返回的 IBinder 对象创建 Messenger 对象并使用此对象向服务端发送消息即可完成进程间通信。
*/
public class ClientActivity extends AppCompatActivity {
private static final String TAG = "cfmtest";
public static final int MSG_FROM_CLIENT = 1;
private Messenger mMessenger;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 根据服务端返回的 IBinder ,创建一个 Messenger 对象
mMessenger = new Messenger(service);
// 通过该 Messenger 对象,发送 Message 消息给服务端,服务端即可根据 Message 的信息作相应的处理
Message msg = Message.obtain();
msg.what = MSG_FROM_CLIENT;
Bundle bundle = new Bundle();
bundle.putInt("a", 333);
bundle.putInt("b", 555);
msg.setData(bundle);
Log.d(TAG, "客户端请求服务端计算 333 和 555 相加的结果!");
try {
mMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
// 绑定远程服务
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
AndroidManifest.xml:
...
<service
android:name=".MessengerService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
...
Log 打印信息:
客户端:
com.cfm.messengertest D/cfmtest: 客户端请求服务端计算 333 和 555 相加的结果!
服务端:
com.cfm.messengertest:remote D/cfmtest: 远程服务绑定成功!
com.cfm.messengertest:remote D/cfmtest: 接收到了客户端发起的请求!
com.cfm.messengertest:remote D/cfmtest: 服务端计算两个数相加的结果为:888
通过上面的例子可以得出:
1. 在 Messenger 中进行数据传输必须将数据放入 Message 中,由于 Messenger 和 Message 都实现了 Parcelable 接口,所以它们都可以进行跨进程传输。故 Message 所支持的数据类型就是 Messenger 中所支持的数据传输类型。
2. Message 中能使用的载体包含 what、arg1、agr2、Bundle 和 replyTo。
3. Message 中的另一个字段 object 在同一个进程中很实用,但是在进程间通信的时候,在 Android 2.2 以前 object 字段不支持跨进程传输,在 2.2 之后仅支持系统提供的并实现了 Parcelable 接口的对象才能通过它来传输。所以我们自定义的实现了 Parcelable 接口的对象无法通过 object 字段来传输,但是我们可以通过 Bundle 对象来实现。
eg2:客户端向服务端发送两个 int 型整数,并请求服务端计算两个整型数的结果,然后服务端将计算的结果返回给客户端。
远程服务端 MessengerService.java:
package com.cfm.messengertest;
/**
* 步骤1. 在服务端创建一个 Service 来处理客户端的连接请求
*/
public class MessengerService extends Service {
private static final String TAG = "cfmtest";
public static final int MSG_FROM_SERVER = 2;
/**
* 步骤2. 创建一个执行客户端的请求的 Handler 类
*/
private static class MessengerHandler extends Handler {
// 执行客户端请求的回调方法
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ClientActivity.MSG_FROM_CLIENT:
Log.d(TAG, "接收到了客户端发起的请求!");
// 获取客户端发过来的 Messenger 对象,然后通过该对象向客户端回复消息
// 此时与这个 messenger 相关联的 handler 就是客户端的 handler
Messenger messenger = msg.replyTo;
int result = msg.getData().getInt("a") + msg.getData().getInt("b");
Bundle bundle = new Bundle();
bundle.putInt("result", result);
Message replyMsg = Message.obtain(null, MSG_FROM_SERVER);
replyMsg.setData(bundle);
try {
messenger.send(replyMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
/**
* 步骤3: 通过步骤 2 创建一个 Handler 对象,然后创建一个与之相关联的 Messenger
*/
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "远程服务绑定成功!");
/**
* 步骤4: 获取该 Messenger 对象的底层 Binder,然后通过 Binder 完成进程间通信。
*/
return mMessenger.getBinder();
}
}
客户端 ClientActivity.java:
package com.cfm.messengertest;
/**
* 客户端需要绑定远程服务,然后根据服务端返回的 IBinder 对象创建 Messenger 对象并使用此对象向服务端发送消息即可完成进程间通信。
*/
public class ClientActivity extends AppCompatActivity {
private static final String TAG = "cfmtest";
public static final int MSG_FROM_CLIENT = 1;
private Messenger mMessenger;
/**
* 创建一个处理服务端回复的 Handler,在客户端作相应的逻辑处理。
*/
private static class MessengerHandlerForClient extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_FROM_SERVER:
// 处理服务端回复的请求
int result = msg.getData().getInt("result");
Log.d("cfmtest", "客户端接收到服务端返回的计算结果: " + result);
break;
default:
super.handleMessage(msg);
}
}
}
/**
* 创建一个与客户端 Handler 相关联的 Messenger 对象
*/
private Messenger mClientReplyMessenger = new Messenger(new MessengerHandlerForClient());
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 根据服务端返回的 IBinder ,创建一个 Messenger 对象
mMessenger = new Messenger(service);
// 通过该 Messenger 对象,发送 Message 消息给服务端,服务端即可根据 Message 的信息作相应的处理
Message msg = Message.obtain();
msg.what = MSG_FROM_CLIENT;
Bundle bundle = new Bundle();
bundle.putInt("a", 333);
bundle.putInt("b", 555);
msg.setData(bundle);
Log.d(TAG, "客户端请求服务端计算 333 和 555 相加的结果!");
// 服务端可以获取到该 Messenger 对象,然后通过该对象向客户端发送消息
msg.replyTo = mClientReplyMessenger;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
// 绑定远程服务
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
AndroidManifest.xml:
...
<service
android:name=".MessengerService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
...
Log 打印信息:
客户端:
com.cfm.messengertest D/cfmtest: 客户端请求服务端计算 333 和 555 相加的结果!
com.cfm.messengertest D/cfmtest: 客户端接收到服务端返回的计算结果: 888
服务端:
com.cfm.messengertest:remote D/cfmtest: 远程服务绑定成功!
com.cfm.messengertest:remote D/cfmtest: 接收到了客户端发起的请求!
最后来一张刚哥的总结图:Messenger 的工作原理图
综上:
可以看到通过 Messenger 实现的跨进程通信功能一般,但是支持一对多串行通信(因为它一次只处理一个消息),并且支持实时通信。但是缺点是它不能很好的处理高并发的情形,不支持 RPC(Remote Procedure Call 远程过程调用),因为数据通过 Message 进行传输,因此只能传输 Bundle 支持的数据类型。所以这种 IPC 方式适用的场景为低并发的一对多即时通信,无 RPC 需求或者不需要返回结果的 RPC 需求。