Messenger 可以用于进程间通讯,其底层就是使用 AIDL 实现。Messenger 封装了一个IMessenger 类型的属性 mTarget ,它是 Messenger 实现进程间通讯的核心类。
1、创建 Messenegr
上面的描述可能有点模糊,没关系先来了解一下如何创建 Messenegr :
- 如果想让 Messenger 作为服务端接收客户端的消息,那么创建 Messenger 对象的方式就是调用带有 Handler 的构造方法。看了下面的源码我们大概知道 mTarget 就是 IMessenger 真正实现类,而这里得 mTarget 就是 MessengerImpl 对象。
这里涉及到一个 Handler ,它得作用是什么呢?因为客户端需要给服务端发送消息,而学过 AIDL 的同学都知道,客户端其实操作的是服务端远程接口的代理对象,真正实现功能就是 MessengerImpl 这个对象,代理对象实际通过 Binder 调用 MessengerImpl 中真正的方法去发送 message,而 Handler 就是用于调度 message 信息。
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
- 如果想让 Messenger 作为客户端给服务端发送消息,那么创建 Messenger 对象的方式就是调用带有 IBinder 的构造方法,其内部将 IBinder 转化为 IMessenger 的代理对象。
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
我们在上面讲了,客户端给服务端发送消息实际上操作的是远程服务接口的代理对象 mTarget ,它内部会调用远程接口的真正实现类 MessengerImpl 完成消息的发送。
2、服务端与客户端通讯示例代码
2.1、服务端实现
- 根据上面的知识点描述,作为服务端的 Messenger 是通过带有 Handler 的构造方法去创建的,这时相当把远程服务的真正实现类 MessengerImple 创建好了,而 mTarget 就是指向该 MessengerImpl 对象。
public Messenger(Handler target) {
//真正实现类
mTarget = target.getIMessenger();
}
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
//实际返回的是 MessengerImpl 对象
mMessenger = new MessengerImpl();
return mMessenger;
}
}
- messenger.getBinder()将该实现类转化为 Binder 传递给客户端。
public IBinder getBinder() {
//将第一步创建的 mTarget 实体对象转化为 Binder
return mTarget.asBinder();
}
- 实现Handler接收客户端发送过来的 Message 消息。
- 获取客户端传递过来的消息;
- msg.replyTo获取客户端带过来的 Messenger 对象 client;
- 构造一个你要发送的消息;
- 通过 client 去发送这个消息。
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
//接受客户端传递过来的消息
Bundle bundle = msg.getData();
System.out.println(bundle.getString("msg"));
//回应客户端
//拿到这个 msg 对应的 Messenger
Messenger client = msg.replyTo;
//构造一个你要发送的消息
Message message=Message.obtain();
message.what=2;
Bundle data = new Bundle();
data.putString("msg","服务端收到了客户端的消息拉");
message.setData(data);
//通过 Messenger 发送消息给客户端
try {
client.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
- 完整代码实现
/**
* 服务端收到客户端的消息之后,回应客户端
*
*/
public class MessengerService extends Service {
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
//接受客户端传递过来的消息
Bundle bundle = msg.getData();
System.out.println(bundle.getString("msg"));
//回应客户端
//拿到这个 msg 对应的 Messenger
Messenger client = msg.replyTo;
//构造一个你要发送的消息
Message message=Message.obtain();
message.what=2;
Bundle data = new Bundle();
data.putString("msg","服务端收到了客户端的消息拉");
message.setData(data);
//通过 Messenger 发送消息给客户端
try {
client.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
private final Messenger messenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
2.2、客户端实现
- 当前的 Messenger 是作为客户端的,应该通过带有 IBinder 的构造方法去创建 Messenger 对象。在内部会将该 IBinder 转化为IMessenger 的代理对象mServiceMessenger。
//根据服务端传来的 IBinder 对象创建一个 Messenger
mServiceMessenger = new Messenger(service);
//通过 IBinder 创建 Messenger
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
- 创建一个用于服务端给客户端回应消息的 Messenger 载体
private final Messenger mGetReplyMessenger = new Messenger(new GetMessageHandler());
- 用于接收处理服务端回应客户端消息的 Handler
private static class GetMessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 2:
Bundle data = msg.getData();
String result = data.getString("msg");
System.out.println("receive from server:"+result);
break;
}
}
}
- 构建一个 message 对象,并通过该代理对象进行发送,这个过程就是进行 IPC 进程间通讯。想一下这里为什么要使用 message 呢?因为代理对象的 send 方法发送的就是 message 对象,并且 message 实现了 Parcelable 接口,因此可以作为数据的载体进行进程间通讯。而且刚才说过,最终真正发送消息的是服务端远程接口的真正实现类 MessengerImpl 去发送的,并且由 handler 去调度 message,这里的 handler 刚好可以处理 message 消息。这里给 message.replyTo 赋值了一个在客户端创建的 Messenger 对象,目的是为了在服务端能通过该 Messenger 给客户端回应消息。
//当服务建立连接之后就发送数据给服务端
Message msg = Message.obtain();
msg.what = 1;
//跨进程通讯不能使用 message.obj 这个属性,应该使用 bundle 否则会出现Can't marshal non-Parcelable ob
//msg.obj = "say hello to server";
Bundle bundle = new Bundle();
bundle.putString("msg", "say hello to server");
msg.setData(bundle);
//将 Messenger 传递给服务端
msg.replyTo = mGetReplyMessenger;
- 完整代码
public class MainActivity extends AppCompatActivity {
private Messenger mServiceMessenger;
private static class GetMessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 2:
Bundle data = msg.getData();
String result = data.getString("msg");
System.out.println("receive from server:"+result);
break;
}
}
}
private final Messenger mGetReplyMessenger = new Messenger(new GetMessageHandler());
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MessengerService.class);
//绑定服务
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//根据服务端传来的 IBinder 对象创建一个 Messenger
mServiceMessenger = new Messenger(service);
//当服务建立连接之后就发送数据给服务端
Message msg = Message.obtain();
msg.what = 1;
//跨进程通讯不能使用 message.obj 这个属性,应该使用 bundle 否则会出现Can't marshal non-Parcelable objects across processes.
//msg.obj = "say hello to server";
Bundle bundle = new Bundle();
bundle.putString("msg", "say hello to server");
msg.setData(bundle);
//将 Messenger 传递给服务端
msg.replyTo = mGetReplyMessenger;
//给远程服务发送消息
try {
mServiceMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if (conn != null) {
unbindService(conn);
}
}
}
3、使用 Message.replyTo 属性将 Messenger 传递给服务端
这里涉及到给客户端回应消息的情况,其过程大概是这样的,首先客户端给服务端发送消息 message ,并且在该 message.replyTo 属性添加在客户端创建的 Messenger 对象(实际上这时的 Messenger 就是 IMessener 的真正实现者),它内部维护了一个 handler 对象,当服务端获取到这个客户端通过 message 传递过来的 Messenger 对象(实际上就是 IMessenger 的代理对象)之后,构造一个 message 对象(服务端回应给客户端的消息)并通过 messenger 发送给客户端。这里要注意:Messenger 是实现了 Parcelable 接口,因此它可以在进程间进行传输。
public final class Messenger implements Parcelable
我们都知道进程间通讯就是对传输的数据进行序列化和反序列化的过程,而 Messenger 是可以进行进程间传输的,我们在最开始的时候提过, Messenger 实际上是对 IMessenger 的封装,所以将 Messenger 传递给服务端,我们应该更加关注其封装的 IMessnger 是怎么被被转化为 IBinder 对象传递过去。因此我们想要了解 Messenger 是怎么被传输的,那么直接看 replyTo 携带的 Messenger 的内部是怎么实现 Parcelable 的序列化和反序列化就好了
- 序列化
public void writeToParcel(Parcel dest, int flags) {
if (callback != null) {
throw new RuntimeException(
"Can't marshal callbacks across processes.");
}
dest.writeInt(what);
dest.writeInt(arg1);
dest.writeInt(arg2);
if (obj != null) {
try {
Parcelable p = (Parcelable)obj;
dest.writeInt(1);
dest.writeParcelable(p, flags);
} catch (ClassCastException e) {
throw new RuntimeException(
"Can't marshal non-Parcelable objects across processes.");
}
} else {
dest.writeInt(0);
}
dest.writeLong(when);
dest.writeBundle(data);
//将replyTo这个 Messenger 进行序列化
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
dest.writeInt(sendingUid);
}
- writeMessengerOrNullToParcel
下面代码就是将 Messenger 内部的 mTarget 转化为 IBinder 进行远程传输。
public static void writeMessengerOrNullToParcel(Messenger messenger,
Parcel out) {
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
: null);
}
- 反序化
Messenger 序列化过程就是将 IMessenger 实体转化为 IBinder ,而反序列化就是将获取到的 IBinder 转化未 IMessenger 对象的过程(在 Messenger 构造实现)。
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
return b != null ? new Messenger(b) : null;
}
4、知识点
- 了解使用 Messenger 的基本步骤。
- Messenger 充当客户端和服务端时内部的 mTarget 的实现是不一样的。
- 了解 Messenger 是怎么发送消息。
- Messenger 也是可以进行传输的,但是需要了解内部是怎么处理 message.replyTo 属性。
- Messenger 是串行执行的,服务端一次处理一个客户端请求。
- Messenger 只适合做到传递消息,而不能做到调用服务端的方法。
- Messenger 传输数据的载体是 Message 对象,而需要注意 message 的 obj 属性只能用于系统定义的 Parcelable 实现类。