概念
让服务与远程进程通信,可使用 Messenger 为服务提供接口。通过 Messenger 发送消息(Message)来实现进程间的通信 (IPC)。
Messenger 实现双向通信及示例
通信流程
- 双方实例化
MessengerActivity和Service两边各自都要实例化Messenger,分别为:AMessenger和SMessenger。 Activity到Service的通信Service在onBind时,返回SMessenger的IBinder。Activity在bindService连接后,通过该IBinder得到SMessenger实例,并通过它来向Service发送消息。Service到Activity的通信(回调)Activity组建消息Message并将AMessenger存放消息中,再通过SMessenger实例发送消息时传递给Service,Service通过拿到的AMessenger实例发送消息,形成对Activity的回调。
服务端
- 定义各种
Message的ID
1 | // 从客户端来的消息 |
- 自定义
Handler并实例化SMessenger
接受来自客户端的消息,后台处理:
1 | /** Keeps track of all current registered clients. */ |
onBind返回Messenger
1 |
|
- 服务端向客户端发送消息(回调)
1 | private void feedbackToClient(Messenger replyTo){ |
客户端
- 自定义
Handler并实例化AMessenger
接受来自服务端的消息,响应回调:
1 | private class IncomingFromServiceHandler extends Handler { |
- 绑定服务后获取
SMessenger
其中mService即从服务器端获取到的SMessenger:
1 | /** Flag indicating whether we have called bind on the service. */ |
- 客户端向服务端发送消息
也就是客户端有两个Messenger的实例,一个用于响应回调的AMessenger,一个用于发送消息的SMessenger:
1 | private void doLongRunningWork(){ |
示例及 Log 分析
客户端向服务端下发耗时任务,并响应服务端的回调
1 | // 启动客户端 Activity |
客户端同时下发三条耗时任务
下发了三条耗时任务,服务端串行接收这些消息并处理。耗时任务是新建线程执行的,可以等待该线程执行完了再接受消息(即串行执行耗时任务),也可以接受消息时再开启新开线程执行(即并行执行耗时任务),示例是并行执行耗时任务:
1 | // 绑定服务 |
自定义 Parcelable 数据传输
进程间通信时,Messenger 在传递数据时,需要通过 Bundle 将自定义的数据打包传输。但是另一个进程在取出 Bundle 时的 ClassLoader 为 null,会使用系统默认值 BootClassLoader 来加载自定义的 Parcelable 数据,所以会出现异常找不到该数据类。如果是在相同进程中使用 Messenger 则不会出现这个问题,不过如果是相同进程也不需要使用 Messenger 来通信了。
- 解决方案
在取出Parcelable数据前,设置该应用为ClassLoader的值:
1 | if(msg != null && msg.getData() != null){ |
- 异常
ClassNotFoundException异常Log:
1 | 14:50:33.803 14568-14568/:messenger D/AndroidRuntime: Shutting down VM |
源码分析
从源码可以看出:Messenger 的本质还是使用的 AIDL,只是封装后更方便使用。
1 | // Messenger.java |
Messenger 和 Message 的区别和联系
从源码中可以看出,Message 是一个包含各种数据的容器,Messenger 可以作为 Parcelable 数据,通过 Message 装载后传递出去。而从 Messenger 的 AIDL 来看,它唯一的方法就是发送 Message,是一名信差。
Messenger 总结
- 本质还是
AIDL - 只能异步通信,同步的话必须使用
AIDL - 因为使用的是
Handler和Message,所以所有的消息都是串行接受和处理的,处理的过程可以是多线程或单线程来处理