Android 进阶10:进程通信之 Messenger 使用与解析
读完本文你将了解:
前面我们介绍了 AIDL 的使用与原理,这篇文章来介绍下 Android 中另一种 IPC 方式:Messenger。
Messenger 简介
Messenger “信使”,顾名思义,它的作用就是传递信息。
Messenger 有两个构造函数:
- 以 Handler 为参数
- 以 Binder 为参数
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target); //和前面的 AIDL 很相似吧
}
看下 Handler.getIMessenger() 源码:
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
这个 IMessanger 应该也是个 AIDL 生成的类吧,看下源码,果然是:
public interface IMessenger extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
android.os.IMessenger {
private static final java.lang.String DESCRIPTOR = "android.os.IMessenger";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static android.os.IMessenger asInterface(...}
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {...}
private static class Proxy implements android.os.IMessenger {...}
public void send(android.os.Message msg)
throws android.os.RemoteException;
}
IMessenger 是 AIDL 生成的跨进程接口,里面定义了一个发送消息的方法:
public void send(android.os.Message msg)
throws android.os.RemoteException;
Handler 中 MessengerImpl 实现了这个方法,就是使用 Handler 将消息发出去:
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
这就解释了为什么我们的消息来得时候会出现在 Handler.handlerMessage() 中
接着再看下 Messenger 另一个重要的方法,send():
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
Messenger 中持有一个 IMessenger 的引用,在构造函数中可以通过 Handler 或者 Binder 的形式获得最终的 IMessenger 实现,然后调用它的 send() 方法。
Messenger 其实就是 AIDL 的简化版,它把接口都封装好,我们只需在一个进程创建一个 Handler 传递给 Messenger,Messenger 帮我们把消息跨进程传递到另一个进程,我们在另一个进程的 Handler 在处理消息就可以了。
Messenger 的使用
Messenger 的使用需要结合 Handler, Message, Bundle 。
下面我们将写一个客户端跨进程发送消息到服务端的例子,服务端在收到消息后会回复,由于在 Messenger 中一个对象对应一个 Handler,所以我们需要在客户端、服务端分别创建一个 Messenger:
服务端在收到消息后会使用 Message.replyTo 对应的信使回复消息。
服务端
服务端只需要创建一个 Messenger 对象,然后给它传递一个 Handler,在 Handler 中处理消息:
public class MessengerService extends BaseService {
private final String TAG = this.getClass().getSimpleName();
Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(final Message msg) {
if (msg != null && msg.arg1 == ConfigHelper.MSG_ID_CLIENT) {
if (msg.getData() == null) {
return;
}
String content = (String) msg.getData().get(ConfigHelper.MSG_CONTENT); //接收客户端的消息
LogUtils.d(TAG, "Message from client: " + content);
//回复消息给客户端
Message replyMsg = Message.obtain();
replyMsg.arg1 = ConfigHelper.MSG_ID_SERVER;
Bundle bundle = new Bundle();
bundle.putString(ConfigHelper.MSG_CONTENT, "听到你的消息了,请说点正经的");
replyMsg.setData(bundle);
try {
msg.replyTo.send(replyMsg); //回信
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
@Nullable
@Override
public IBinder onBind(final Intent intent) {
return mMessenger.getBinder();
}
}
客户端
public class IPCTestActivity extends BaseActivity {
private final String TAG = this.getClass().getSimpleName();
@BindView(R.id.tv_result)
TextView mTvResult;
@BindView(R.id.btn_add_person)
Button mBtnAddPerson;
@BindView(R.id.et_msg_content)
EditText mEtMsgContent;
@BindView(R.id.btn_send_msg)
Button mBtnSendMsg;
/**
* 客户端的 Messenger
*/
Messenger mClientMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(final Message msg) {
if (msg != null && msg.arg1 == ConfigHelper.MSG_ID_SERVER){
if (msg.getData() == null){
return;
}
String content = (String) msg.getData().get(ConfigHelper.MSG_CONTENT);
LogUtils.d(TAG, "Message from server: " + content);
}
}
});
//服务端的 Messenger
private Messenger mServerMessenger;
private ServiceConnection mMessengerConnection = new ServiceConnection() {
@Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
mServerMessenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(final ComponentName name) {
mServerMessenger = null;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
ButterKnife.bind(this);
bindAIDLService();
bindMessengerService();
}
private void bindMessengerService() {
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mMessengerConnection, BIND_AUTO_CREATE);
}
@OnClick(R.id.btn_send_msg)
public void sendMsg() {
String msgContent = mEtMsgContent.getText().toString();
msgContent = TextUtils.isEmpty(msgContent) ? "默认消息" : msgContent;
Message message = Message.obtain();
message.arg1 = ConfigHelper.MSG_ID_CLIENT;
Bundle bundle = new Bundle();
bundle.putString(ConfigHelper.MSG_CONTENT, msgContent);
message.setData(bundle);
message.replyTo = mClientMessenger; //指定回信人是客户端定义的
try {
mServerMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mMessengerConnection);
}
}
运行效果
发送后,服务端进程收到消息:
然后进行了答复:
使用小结
可以看到客户端的操作主要有 3 步:
- 创建客户端的
Messenger,传递一个Handler处理消息 bindService,在ServiceConnection回调中拿到服务端的Messenger- 发送消息
Message.obtain()消息池里获取一个空闲消息对象- 使用
message.setData(bundle)设置数据 - 指定回信的信使
message.replyTo = mClientMessenger - 调用服务端信使,发射!
mServerMessenger.send(message)
总结
Messenger 对 AIDL 进行了封装,也就是对 Binder 的封装,我们可以使用它的实现来完成基于消息的跨进程通信,就和使用 Handler 一样简单。
使用步骤:
- 客户端创建一个
Messenger,传递一个 Handler 处理消息 - 服务端也一样
如果需要回信,给 Message 设置一个用于回信的 Messenger 即可:
message.replyTo = mClientMessenger;
客户端在调用send()方法之后,就会走 Binder 跨进程通信机制 ,最后到服务端的 Handler 中得到处理。
借用鸿洋的图表达一下:
使用时和 Binder 一样,建议在四大组件中使用,那样可以提高优先级,让系统不随便关闭当前进程。
代码地址
Thanks
https://developer.android.com/reference/android/os/Messenger.html
http://book2s.com/java/src/package/android/os/imessenger.html
http://www.jianshu.com/p/af8991c83fcb
http://blog.csdn.net/lmj623565791/article/details/47017485
Android 进阶10:进程通信之 Messenger 使用与解析的更多相关文章
- 图文详解 Android Binder跨进程通信机制 原理
图文详解 Android Binder跨进程通信机制 原理 目录 目录 1. Binder到底是什么? 中文即 粘合剂,意思为粘合了两个不同的进程 网上有很多对Binder的定义,但都说不清楚:Bin ...
- 跨进程通信之Messenger
1.简介 Messenger,顾名思义即为信使,通过它可以在不同进程中传递Message对象,通过在Message中放入我们需要的入局,就可以轻松实现数据的跨进程传递了.Messenger是一种轻量级 ...
- AIDL/IPC Android AIDL/IPC 进程通信机制——超具体解说及使用方法案例剖析(播放器)
首先引申下AIDL.什么是AIDL呢?IPC? ------ Designing a Remote Interface Using AIDL 通常情况下,我们在同一进程内会使用Binder.Broad ...
- Android上的进程通信(IPC)机制
Interprocess Communication Android offers a mechanism for interprocess communication (IPC) using rem ...
- Android系统init进程启动及init.rc全解析
转:https://blog.csdn.net/zhonglunshun/article/details/78615980 服务启动机制system/core/init/init.c文件main函数中 ...
- Android 进阶16:IntentService 使用及源码解析
It's time to start living the life you've only imagined. 读完本文你将了解: IntentService 简介 IntentService 源码 ...
- Android 进阶13:几种进程通信方式的对比总结
不花时间打基础,你将会花更多时间解决那些不必要的问题. 读完本文你将了解: RPC 是什么 IDL 是什么 IPC 是什么 Android 几种进程通信方式 如何选择这几种通信方式 Thanks RP ...
- 详解 CmProcess 跨进程通信的实现
CmProcess 是 Android 一个跨进程通信框架,整体代码比较简单,总共 20 多个类,能够很好的便于我们去了解跨进程实现的原理. 个人猜测 CmProcess 也是借鉴了 VirtualA ...
- Android 进阶7:进程通信之 AIDL 的使用
读完本文你将了解: AIDL 是什么 AIDL 支持的数据类型 AIDL 如何编写 AIDL 实例 创建 AIDL 编写服务端代码 编写客户端代码 运行结果 总结 代码地址 Thanks 记得 201 ...
随机推荐
- HDU - 6435 Problem J. CSGO 2018 Multi-University Training Contest 10 (二进制枚举+思维)
题意:有N个主武器(MW)和M个副武器(SW),每个武器都有自己的S值,和K个附加属性xi.要选取一对主副武器搭配,搭配后获得的性能由该公式得出: 求获得最大的性能为多少. 分析:由于|xm - xs ...
- HDU 6351 (Beautiful Now) 2018 Multi-University Training Contest 5
题意:给定数N(1<=N<=1e9),k(1<=k<=1e9),求对N的任意两位数交换至多k次能得到的最小与最大的数,每一次交换之后不能出现前导零. 因为N最多只有10位,且给 ...
- 剑指offer编程题66道题 36-66
36.两个链表的第一个公共节点 题目描述 输入两个链表,找出它们的第一个公共结点. 1.具有重合节点的两个链表是一个Y字性,用两个堆栈放这两个链表,从尾部开始遍历,直到遍历到最后一个重合节点. 这种算 ...
- React-native Android环境搭建
基础安装 安装Homebrew Homebrew是Mac OSX的包管理器,我们需要通过Homebrew安装开发React Native的相关软件包. 如果不知道怎样安装Homebrew可以点这里:官 ...
- 【WIN7】windows\system32 下的几乎所有文件的简单说明【1】
1: aclui.dll .....Security Descriptor Editor,没有它,注册表编缉器会无法运行 2: ACTIVEDS.DLL .....(ADs 路由层 DLL). 没有它 ...
- React Native集成Redux框架讲解与应用
学过React Native的都知道,RN的UI是根据相应组件的state进行render的,而页面又是由大大小小的组件构成,导致每个组件都必须维护自身的一套状态,因此当页面复杂化的时候,管理stat ...
- 解析Linux系统的平均负载概念
一.什么是系统平均负载(Load average)? 在Linux系统中,uptime.w.top等命令都会有系统平均负载load average的输出,那么什么是系统平均负载呢?系统平均负载被定义为 ...
- 关于 Content-Type:application/x-www-form-urlencoded 和 Content-Type:multipart/related(转)
最近项目中用到的一个是用一个页面接收c程序post过来的一断字符串..总接收不到值... 我用C#写一个测试可以正常接收到值. 最后抓包比较 区别只是 Content-Type:application ...
- JSR规范整理
Web Service技术 Java Date与Time API ( JSR 310) Java API for RESTful Web Services (JAX-RS) 1.1 (JSR 311) ...
- Mybatis plus 高级
最近项目重构 dao层使用的Mybatis plus,有必要总结下. Mybatis plus 会自动维护Mybatis 以及 MyBatis-Spring 相关依赖 所以在构建项目时候 只需要引入 ...