Messenger 可以翻译为信使,顾名思义,通过它可以在不同进程中传递 Message 对象,在 Message 中加入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。Messenger 是一种轻量的 IPC 方案,它的底层实现是 AIDL,下面是 Messenger 的两个构造,从构造方法的实现上我们可以明显看出 AIDL 的痕迹,不管是 Messenger 还是 Stub.asInterface,这种使用方法都表明它的底层是 AIDL。
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
Messenger 的使用方法很简单,它对 AIDL 做了封装,是的我们可以更简单的进行进程间通信。同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为服务端中不存在并发执行的情形。实现一个 Messenger 有如下几个步骤,分为服务端和客户端:
(1)服务端进程
首先,我们需要在服务端创建一个 Service,来处理客户端的连接请求,同时创建一个 Handler 并通过它来创建一个 Messenger 对象,然后在 Service 的 onBind 中返回这个 Messenger 对象底层的 Binder 即可。
(2)客户端进程
客户端进程中,首先要绑定服务端的 Service,绑定成功后用服务端返回的 IBinder 对象创建一个 Messenger,通过 Messenger 就可以向服务端发送消息了,发消息类型为 Message 对象。如果需要服务端能回应客户端,就和服务端一样,我们还需要创建一个 IBinder 并创建一个新的 Messenger,并把这个 Messenger 对象通过 Message 的 replyTo 参数传递给服务端,服务端通过 replyTo 参数就可以回应客户端。首先我们看一个简单的例子,在这个例子中服务端无法回应客户端。
首先看服务端的代码,可以看到 MessengerHandler 用来处理客户端发送的消息,并从消息中取出客户端发来的文本信息。而 mMessenger 是一个 Messenger 对象,它和 MessengerHandler 相关联,并在 onBind 中返回它的 Binder 对象,可以看出,这里 Messenger 的作用是将客户端发送来的消息传递给 MessengerHandler 处理。
public class MessengerService extends Service {
public final String TAG = this.getClass().getName();
private class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
Log.i(TAG, "receive msg from client:" + msg.getData().getString("msg"));
}
}
}
private Messenger messenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
当然,不要忘记在 AndroidManifest 中注册:
<service
android:name=".service.MessengerService"
android:process=":remote"></service>
接下来我们看客户端的代码,客户端的实现也比较简单,肯定需要绑定远程服务的 MessengerService,绑定成功后,根据服务端发送的 Binder 对象创建 Messenger 对象并根据这个对象向服务端发送消息,下面代码正在 Bundle 中向服务端发送了一句话,在上面的服务端会打印这句话。
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putString("msg", "Hello,this is client.");
msg.setData(bundle);
msg.what = 1;
try {
messenger.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_main);
bindService(new Intent(this, MessengerService.class), connection, Context.BIND_AUTO_CREATE);
}
运行之后,我们来看下 log 日志:
09-12 14:33:33.771 23040-23040/com.demo.text.demotext:remote I/com.demo.text.demotext.service.MessengerService: receive msg from client:Hello,this is client.
通过上面的例子可以看出,在 Messenger 中进行数据传递必须将数据放入 Message 中, 而 Messenger 和 Message 都实现了 Parcelable 接口,因此可以跨进程传输。简单来说,Message 所支持的数据类型就是 Messenger 所支持的传输类型。实际上,通过 Messenger 来传输 Message,Message 中使用的载体只有 what、arg1、arg2、Bundle 以及 replyTo。Message 中的另一个字段 object 在同一个进程中是很实用的,但是在进程间通信的时候,在 Android 2.2以前 object 字段不支持进程间通信,即使 2.2 以后,也仅仅是系统提供的实现了 Parcelable 接口的对象才能通过它来传输,这就意味着我们自定义的 Parcelable 对象是无法通过 object 字段来传输的。
上面的例子演示了如何在服务端接收客户端发来的信息,但是有时候我们还需要能够回应客户端,下面介绍如何实现这种效果,还是采用上面的例子,稍微做一下修改,每当客户端发来一条信息,服务端就会自动恢复一套。
首先看服务端的修改,服务端只需要修改 MessengerHandler,当收到消息后,会立即回复一条给客户端:
private class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
Log.i(TAG, "receive msg from client:" + msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message replyMsg = new Message();
replyMsg.what = 1;
Bundle bundle = new Bundle();
bundle.putString("reply", "嗯,你的消息我已经收到,马上回复你。");
replyMsg.setData(bundle);
try {
client.send(replyMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
接着再看客户端的修改,为了接收服务端的回复,客户端也需要准备一个接收消息的 Messenger 的 Handler,如下所示
private Messenger replyMessenger = new Messenger(new MessaengerHandler());
private class MessaengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
Log.i(MainActivity.this.getClass().getName(), "receiver msg from service:" + msg.getData().getString("reply"));
}
}
}
除了上述修改,还有关键的一点,当客户端发送消息的时候,需要把接收服务端回复的 Messenger 通过 Message 的 replyTo 参数传递给服务器,如下所示:
Messenger messenger = new Messenger(service);
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putString("msg", "Hello,this is client.");
msg.setData(bundle);
msg.what = 1;
try {
msg.replyTo = replyMessenger;
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
通过上述修改,我们在运行程序,查看 log 日志:
/com.demo.text.demotext:remote I/com.demo.text.demotext.service.MessengerService: receive msg from client:Hello,this is client.
/com.demo.text.demotext I/com.demo.text.demotext.MainActivity: receiver msg from service:嗯,你的消息我已经收到,马上回复你。
到这里,我们已经把采用 Messenger 实现进程间通信的方式都介绍完了,下面给出一张 Messenger 的工作原理图,方便更好地理解 Messenger,如下所示: