Messenger 详解

概念

让服务与远程进程通信,可使用 Messenger 为服务提供接口。通过 Messenger 发送消息(Message)来实现进程间的通信 (IPC)。

Messenger 实现双向通信及示例

通信流程

  • 双方实例化 Messenger
    ActivityService 两边各自都要实例化 Messenger ,分别为:AMessengerSMessenger
  • ActivityService 的通信
    ServiceonBind 时,返回SMessengerIBinderActivitybindService 连接后,通过该 IBinder 得到 SMessenger 实例,并通过它来向 Service 发送消息。
  • ServiceActivity 的通信(回调)
    Activity 组建消息 Message 并将 AMessenger 存放消息中,再通过 SMessenger 实例发送消息时传递给 ServiceService 通过拿到的 AMessenger 实例发送消息,形成对 Activity 的回调。

服务端

  • 定义各种 MessageID
1
2
3
4
5
6
7
// 从客户端来的消息
public static final int MSG_FROM_CLIENT_REGISTER = 20001;
public static final int MSG_FROM_CLIENT_UNREGISTER = 20002;
public static final int MSG_FROM_CLIENT_DO_LONG_RUNNING_WORK = 20003;

// 服务端反馈给客户端的消息
public static final int MSG_FROM_SERVICE_RESULT = 30001;
  • 自定义 Handler 并实例化 SMessenger
    接受来自客户端的消息,后台处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** Keeps track of all current registered clients. */
private ArrayList<Messenger> mClients = new ArrayList<Messenger>();

private class IncomingFromClientsHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "IncomingFromClientsHandler:handleMessage: ");
switch (msg.what) {
case MSG_FROM_CLIENT_REGISTER:
Log.d(TAG, "handleMessage: MSG_FROM_CLIENT_REGISTER");
mClients.add(msg.replyTo);
break;
case MSG_FROM_CLIENT_UNREGISTER:
Log.d(TAG, "handleMessage: MSG_FROM_CLIENT_UNREGISTER");
mClients.remove(msg.replyTo);
break;
case MSG_FROM_CLIENT_DO_LONG_RUNNING_WORK:
Log.d(TAG, "handleMessage: MSG_FROM_CLIENT_DO_LONG_RUNNING_WORK");
doLongRunningWork(msg);
break;
default:
super.handleMessage(msg);
}
}
}

final Messenger mSMessenger = new Messenger(new IncomingFromClientsHandler());
  • onBind 返回 Messenger
1
2
3
4
5
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return mSMessenger.getBinder();
}
  • 服务端向客户端发送消息(回调)
1
2
3
4
5
6
7
8
9
10
11
12
13
private void feedbackToClient(Messenger replyTo){
Log.d(TAG, "feedbackToClient: ");
Message msg = Message.obtain(null, MSG_FROM_SERVICE_RESULT);
MyDataParcelable data = RandomData.genMyDataParcelable();
Bundle bundle = new Bundle(MyDataParcelable.class.getClassLoader());
bundle.putParcelable(IPC_PARCELABLE_DATA_KEY ,data);
msg.setData(bundle);
try {
replyTo.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}

客户端

  • 自定义 Handler 并实例化 AMessenger
    接受来自服务端的消息,响应回调:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private class IncomingFromServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "IncomingFromServiceHandler:handleMessage: ");
switch (msg.what) {
case MessengerService.MSG_FROM_SERVICE_RESULT:
Log.d(TAG, "handleMessage: MSG_FROM_SERVICE_RESULT");
showMsgFromServiceResult(msg);
break;
default:
super.handleMessage(msg);
}
}
}

/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mAMessenger = new Messenger(new IncomingFromServiceHandler());
  • 绑定服务后获取 SMessenger
    其中 mService 即从服务器端获取到的 SMessenger
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** Flag indicating whether we have called bind on the service. */
private boolean mBound = false;
/** Messenger for communicating with service. */
private Messenger mService;
private ServiceConnection mMessengerServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: Bound!");
mBound = true;
// 拿到服务器端的 SMessenger
mService = new Messenger(service);
}
...
};
  • 客户端向服务端发送消息
    也就是客户端有两个 Messenger 的实例,一个用于响应回调的 AMessenger,一个用于发送消息的 SMessenger
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void doLongRunningWork(){
Log.d(TAG, "doLongRunningWork: ");
Message msg = Message.obtain(null,
MessengerService.MSG_FROM_CLIENT_DO_LONG_RUNNING_WORK);
Bundle bundle = new Bundle(MyDataParcelable.class.getClassLoader());
MyDataParcelable data = RandomData.genMyDataParcelable();
bundle.putParcelable(MessengerService.IPC_PARCELABLE_DATA_KEY, data);
msg.setData(bundle);
// 客户端的 AMessenger,传递给服务端后用于回调
msg.replyTo = mAMessenger;
if(mService != null){
try {
// SMessenger 发送消息给服务端
mService.send(msg);
} catch (RemoteException e){
e.printStackTrace();
}
}
}

示例及 Log 分析

客户端向服务端下发耗时任务,并响应服务端的回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 启动客户端 Activity
17:24:16.964 30355-30355/D:ShowIPCActivity:: onCreate:
// 绑定服务
17:24:18.228 30355-30355/D:ShowIPCActivity:: onClick: button.onClick = Bind Service
17:24:18.228 30355-30355/D:ShowIPCActivity:: bindMessengerService:
17:24:18.229 30355-30355/D:ShowIPCActivity:: bindMessengerService: Binding...
// 服务端启动
17:24:18.246 30355-30355/D:MessengerService:: onCreate:
17:24:18.246 30355-30355/D:MessengerService:: onBind:
// 连接服务端
17:24:18.252 30355-30355/D:ShowIPCActivity:: onServiceConnected: Bound!
17:24:18.253 30355-30355/D:ShowIPCActivity:: registerClient:
17:24:18.253 30355-30355/D:MessengerService:: IncomingFromClientsHandler:handleMessage:
17:24:18.253 30355-30355/D:MessengerService:: handleMessage: MSG_FROM_CLIENT_REGISTER
// 下发耗时任务
17:24:20.886 30355-30355/D:ShowIPCActivity:: onClick: button.onClick = LongRunningWork
17:24:20.886 30355-30355/D:ShowIPCActivity:: doLongRunningWork:
17:24:20.892 30355-30355/D:ShowIPCActivity:: doLongRunningWork:
data = name: bic, arrays: 1 2 3 , book.name: icfhd, book.price: 49
// 服务端接受到耗时任务消息
17:24:20.899 30355-30355/D:MessengerService:: IncomingFromClientsHandler:handleMessage:
17:24:20.899 30355-30355/D:MessengerService:: handleMessage: MSG_FROM_CLIENT_DO_LONG_RUNNING_WORK
17:24:20.899 30355-30355/D:MessengerService:: doLongRunningWork:
17:24:20.899 30355-30355/D:MessengerService:: doLongRunningWork:
msg.data = name: bic, arrays: 1 2 3 , book.name: icfhd, book.price: 49
// 服务端新建线程 30467 执行耗时任务
17:24:20.905 30355-30476/D:MessengerService:: MyDataRunnable:run...
17:24:23.908 30355-30476/D:MessengerService:: MyDataRunnable:run: done!
// 服务端执行完任务后,发送消息给客户端(回调)
17:24:23.908 30355-30476/D:MessengerService:: feedbackToClient:
17:24:23.909 30355-30476/D:MessengerService:: feedbackToClient:
data = name: ahf, arrays: 1 2 3 , book.name: ifebd, book.price: 40
// 客户端收到服务端的回调消息并处理
17:24:23.909 30355-30355/D:ShowIPCActivity:: IncomingFromServiceHandler:handleMessage:
17:24:23.909 30355-30355/D:ShowIPCActivity:: handleMessage: MSG_FROM_SERVICE_RESULT
17:24:23.909 30355-30355/D:ShowIPCActivity:: showMsgFromServiceResult:
17:24:23.909 30355-30355/D:ShowIPCActivity:: showMsgFromServiceResult:
msg.data = name: ahf, arrays: 1 2 3 , book.name: ifebd, book.price: 40
// 解除服务绑定
17:24:31.812 30355-30355/D:ShowIPCActivity:: onClick: button.onClick = Unbind Service
17:24:31.812 30355-30355/D:ShowIPCActivity:: unBindMessengerService:
17:24:31.812 30355-30355/D:ShowIPCActivity:: unBindMessengerService: unBind!
17:24:31.812 30355-30355/D:ShowIPCActivity:: unregisterClient:
17:24:31.816 30355-30355/D:MessengerService:: IncomingFromClientsHandler:handleMessage:
17:24:31.816 30355-30355/D:MessengerService:: handleMessage: MSG_FROM_CLIENT_UNREGISTER
// 服务销毁
17:24:31.817 30355-30355/D:MessengerService:: onDestroy:

客户端同时下发三条耗时任务

下发了三条耗时任务,服务端串行接收这些消息并处理。耗时任务是新建线程执行的,可以等待该线程执行完了再接受消息(即串行执行耗时任务),也可以接受消息时再开启新开线程执行(即并行执行耗时任务),示例是并行执行耗时任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 绑定服务
18:45:32.446 30355-30355/D:ShowIPCActivity:: onClick: button.onClick = Bind Service
18:45:32.446 30355-30355/D:ShowIPCActivity:: bindMessengerService:
18:45:32.446 30355-30355/D:ShowIPCActivity:: bindMessengerService: Binding...
18:45:32.455 30355-30355/D:MessengerService:: onCreate:
18:45:32.455 30355-30355/D:MessengerService:: onBind:
18:45:32.457 30355-30355/D:ShowIPCActivity:: onServiceConnected: Bound!
18:45:32.457 30355-30355/D:ShowIPCActivity:: registerClient:
18:45:32.457 30355-30355/D:MessengerService:: IncomingFromClientsHandler:handleMessage:
18:45:32.457 30355-30355/D:MessengerService:: handleMessage: MSG_FROM_CLIENT_REGISTER
// 下发耗时任务 1
18:45:35.196 30355-30355/D:ShowIPCActivity:: onClick: button.onClick = LongRunningWork
18:45:35.196 30355-30355/D:ShowIPCActivity:: doLongRunningWork:
18:45:35.196 30355-30355/D:ShowIPCActivity:: doLongRunningWork:
data = name: eha, arrays: 1 2 3 , book.name: ddaca, book.price: 99
// 服务端接受到耗时任务消息 1
18:45:35.197 30355-30355/D:MessengerService:: IncomingFromClientsHandler:handleMessage:
18:45:35.197 30355-30355/D:MessengerService:: handleMessage: MSG_FROM_CLIENT_DO_LONG_RUNNING_WORK
18:45:35.197 30355-30355/D:MessengerService:: doLongRunningWork:
18:45:35.198 30355-30355/D:MessengerService:: doLongRunningWork:
msg.data = name: eha, arrays: 1 2 3 , book.name: ddaca, book.price: 99
// 新建线程 8198 执行耗时任务1
18:45:35.203 30355-8198/D:MessengerService:: MyDataRunnable:run...
// 下发耗时任务2
18:45:35.418 30355-30355/D:ShowIPCActivity:: onClick: button.onClick = LongRunningWork
18:45:35.418 30355-30355/D:ShowIPCActivity:: doLongRunningWork:
18:45:35.418 30355-30355/D:ShowIPCActivity:: doLongRunningWork:
data = name: hhg, arrays: 1 2 3 , book.name: haace, book.price: 13
// 服务端接受到耗时任务消息2
18:45:35.418 30355-30355/D:MessengerService:: IncomingFromClientsHandler:handleMessage:
18:45:35.418 30355-30355/D:MessengerService:: handleMessage: MSG_FROM_CLIENT_DO_LONG_RUNNING_WORK
18:45:35.418 30355-30355/D:MessengerService:: doLongRunningWork:
18:45:35.418 30355-30355/D:MessengerService:: doLongRunningWork:
msg.data = name: hhg, arrays: 1 2 3 , book.name: haace, book.price: 13
// 新建线程 8199 执行耗时任务2
18:45:35.423 30355-8199/D:MessengerService:: MyDataRunnable:run...
// 下发耗时任务3
18:45:35.648 30355-30355/D:ShowIPCActivity:: onClick: button.onClick = LongRunningWork
18:45:35.648 30355-30355/D:ShowIPCActivity:: doLongRunningWork:
18:45:35.648 30355-30355/D:ShowIPCActivity:: doLongRunningWork:
data = name: gid, arrays: 1 2 3 , book.name: hfbie, book.price: 40
// 服务端接受到耗时任务消息3
18:45:35.648 30355-30355/D:MessengerService:: IncomingFromClientsHandler:handleMessage:
18:45:35.649 30355-30355/D:MessengerService:: handleMessage: MSG_FROM_CLIENT_DO_LONG_RUNNING_WORK
18:45:35.649 30355-30355/D:MessengerService:: doLongRunningWork:
18:45:35.649 30355-30355/D:MessengerService:: doLongRunningWork:
msg.data = name: gid, arrays: 1 2 3 , book.name: hfbie, book.price: 40
// 新建线程 8209 执行耗时任务3
18:45:35.650 30355-8209/D:MessengerService:: MyDataRunnable:run...
// 耗时任务1 线程 8198 执行完毕,并发送回调消息
18:45:38.203 30355-8198/D:MessengerService:: MyDataRunnable:run: done!
18:45:38.203 30355-8198/D:MessengerService:: feedbackToClient:
18:45:38.203 30355-8198/D:MessengerService:: feedbackToClient:
data = name: afi, arrays: 1 2 3 , book.name: ibabe, book.price: 93
18:45:38.204 30355-30355/D:ShowIPCActivity:: IncomingFromServiceHandler:handleMessage:
18:45:38.204 30355-30355/D:ShowIPCActivity:: handleMessage: MSG_FROM_SERVICE_RESULT
18:45:38.204 30355-30355/D:ShowIPCActivity:: showMsgFromServiceResult:
18:45:38.204 30355-30355/D:ShowIPCActivity:: showMsgFromServiceResult:
msg.data = name: afi, arrays: 1 2 3 , book.name: ibabe, book.price: 93
// 耗时任务2 线程 8199 执行完毕,并发送回调消息
18:45:38.423 30355-8199/D:MessengerService:: MyDataRunnable:run: done!
18:45:38.423 30355-8199/D:MessengerService:: feedbackToClient:
18:45:38.423 30355-8199/D:MessengerService:: feedbackToClient:
data = name: ffc, arrays: 1 2 3 , book.name: dcdfd, book.price: 65
18:45:38.424 30355-30355/D:ShowIPCActivity:: IncomingFromServiceHandler:handleMessage:
18:45:38.424 30355-30355/D:ShowIPCActivity:: handleMessage: MSG_FROM_SERVICE_RESULT
18:45:38.424 30355-30355/D:ShowIPCActivity:: showMsgFromServiceResult:
18:45:38.424 30355-30355/D:ShowIPCActivity:: showMsgFromServiceResult:
msg.data = name: ffc, arrays: 1 2 3 , book.name: dcdfd, book.price: 65
// 耗时任务3 线程 8209 执行完毕,并发送回调消息
18:45:38.650 30355-8209/D:MessengerService:: MyDataRunnable:run: done!
18:45:38.650 30355-8209/D:MessengerService:: feedbackToClient:
18:45:38.650 30355-8209/D:MessengerService:: feedbackToClient:
data = name: icc, arrays: 1 2 3 , book.name: ibicd, book.price: 53
18:45:38.651 30355-30355/D:ShowIPCActivity:: IncomingFromServiceHandler:handleMessage:
18:45:38.651 30355-30355/D:ShowIPCActivity:: handleMessage: MSG_FROM_SERVICE_RESULT
18:45:38.651 30355-30355/D:ShowIPCActivity:: showMsgFromServiceResult:
18:45:38.651 30355-30355/D:ShowIPCActivity:: showMsgFromServiceResult:
msg.data = name: icc, arrays: 1 2 3 , book.name: ibicd, book.price: 53

自定义 Parcelable 数据传输

进程间通信时,Messenger 在传递数据时,需要通过 Bundle 将自定义的数据打包传输。但是另一个进程在取出 Bundle 时的 ClassLoadernull,会使用系统默认值 BootClassLoader 来加载自定义的 Parcelable 数据,所以会出现异常找不到该数据类。如果是在相同进程中使用 Messenger 则不会出现这个问题,不过如果是相同进程也不需要使用 Messenger 来通信了。

  • 解决方案
    在取出 Parcelable 数据前,设置该应用为 ClassLoader 的值:
1
2
3
4
5
6
7
8
9
if(msg != null && msg.getData() != null){
Bundle bundle = msg.getData();
if(bundle != null) {
// must set class loader before getParcelable, ClassNotFoundException
// 必须设置 ClassLoader,才能获取 Bundle 中的数据
bundle.setClassLoader(getClassLoader());
MyDataParcelable data = bundle.getParcelable(IPC_PARCELABLE_DATA_KEY);
}
}
  • 异常
    ClassNotFoundException 异常 Log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
14:50:33.803 14568-14568/:messenger D/AndroidRuntime: Shutting down VM
14:50:33.804 14568-14568/:messenger E/AndroidRuntime: FATAL EXCEPTION: main
Process: :messenger, PID: 14568
android.os.BadParcelableException: ClassNotFoundException when unmarshalling: .ipc.MyDataParcelable
at android.os.Parcel.readParcelableCreator(Parcel.java:2295)
at android.os.Parcel.readParcelable(Parcel.java:2245)
at android.os.Parcel.readValue(Parcel.java:2152)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2485)
at android.os.BaseBundle.unparcel(BaseBundle.java:221)
at android.os.Bundle.getParcelable(Bundle.java:755)
at .ipc.MessengerService.doLongRunningWork(MessengerService.java:129)
at .ipc.MessengerService.access$100(MessengerService.java:18)
at .ipc.MessengerService$IncomingFromClientsHandler.handleMessage(MessengerService.java:71)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5344)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)

源码分析

从源码可以看出:Messenger 的本质还是使用的 AIDL,只是封装后更方便使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Messenger.java
public final class Messenger implements Parcelable {
// IMessenger.aidl 实例
private final IMessenger mTarget;

/* Create a new Messenger pointing to the given Handler. */
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}

/* Create a Messenger from a raw IBinder */
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}

...
}

// IMessenger.aidl的具体实现是在 Handler.java 中
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}

// 具体实现,消息的发送是通过 Handler 实现的
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}

MessengerMessage 的区别和联系

从源码中可以看出,Message 是一个包含各种数据的容器,Messenger 可以作为 Parcelable 数据,通过 Message 装载后传递出去。而从 MessengerAIDL 来看,它唯一的方法就是发送 Message,是一名信差。

Messenger 总结

  • 本质还是 AIDL
  • 只能异步通信,同步的话必须使用 AIDL
  • 因为使用的是 HandlerMessage,所以所有的消息都是串行接受和处理的,处理的过程可以是多线程或单线程来处理

参考文档

  1. https://developer.android.com/guide/components/bound-services.html#Binding
  2. http://blog.csdn.net/lmj623565791/article/details/47017485
  3. http://blog.csdn.net/jiwangkailai02/article/details/48098087
  4. http://blog.csdn.net/hyhdcl/article/details/52680301
0%