Android进阶笔记:Messenger源码详解
Messenger可以理解为一个是用于发送消息的一个类用法也很多,这里主要分析一下再跨进程的情况下Messenger的实现流程与源码分析。相信结合前面两篇关于aidl解析文章能够更好的对aidl有一个认识。(Android进阶笔记:AIDL内部实现详解 (一)、Android进阶笔记:AIDL内部实现详解 (二))
用法说明
先来看一下Messenger在跨进程通讯时的使用方法,代码如下:
Service的代码
//用来传递Messenger中IMessenger
public class ServerService extends Service {
public static final String TAG = "ServerService";
private Messenger messenger;
@Override
public void onCreate() {
super.onCreate();
//创建一个Messenger对象
messenger = new Messenger(new MessengerHandler());
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//返回IMessenger
return messenger.getBinder();
}
}
//用于创建Messenger的Handler
class MessengerHandler extends Handler {
public static final String TAG = "ServerService";
@Override
public void handleMessage(Message msg) {
if (msg.what == 10001) {
String data = msg.getData().getString("data");
data = data == null ? "null" : data;
Log.e(TAG, "handleMessage: get msg from client = (" + data + ")");
}
}
}
上面就是Service的代码,分析一下其实总共做了3步:
- 定义了一个MessengerHandler的内部类并且实现了handleMessage的内部回调;
- 创建了一个Messenger的对象,在Messenger的构造函数中传入刚刚创建的handler实例;
- 在Service的onBind方法中回调messenger的getBinder()方法;
Activity的代码:
public class MainActivity extends AppCompatActivity {
private ServiceConnection serviceConnection;
private Messenger messenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("MainActivity", "onServiceConnected: connection success !!!");
//用返回的Ibinder对象来构造一个Messenger实例
messenger = new Messenger(service);
//创建一个msg
Message msg = new Message();
msg.what = 10001;
Bundle bundle = new Bundle();
bundle.putString("data", "hello Server");
msg.setData(bundle);
try {
//调用messenger的send方法
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
messenger = null;
}
};
bindService(new Intent("com.coder_f.messengerdemo.ServerService"), serviceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
Activity中做的工作其实也不复杂也是3步:
- 通过绑定Service来获得IBinder对象;
- 通过IBinder对象重新构造一个Messenger;
- 通过Messenger的send方法来发送消息;
以上就是Messenger的使用方法。好了现在就可以根据上面的使用方法来看看Messenger内部到底是怎么来运作的。
源码解析
首先先来看看参数为Handler的Messenger的构造函数
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
很简单,就是保存传入Handler的getIMessenger()方法返回的东西。那接下来就去看看getIMessenger()方法返回的是什么东西。
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
也不复杂,无非就是判断一下mMessenger是不是空然后返回一下。那mMessenger到底是什么呢。看看它的构造函数new MessengerImpl()代码如下:
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
看到这里相信已经摸的差不多了,这个结构就是aidl的结构。这里猜一下应该就明白了,源码里面肯定有定义了一个IMessage.aidl,而且里面还声明了一个send的方法。而这个seng的方法在Handler里面被实现了,具体就是通过Handler来发送一条消息。那么可以得到结论,最后mTarget获得的其实就是一个IMessenger.Stub的实例,里面已经实现了接口中的方法(send(Message msg))
事实证明确实如此,源码的位置:platform\frameworks\base\core\java\android\os\IMessenger.aidl
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}
上面只是对构造函数的源码进行了分析,但是其实已经把Messenger的结构摸的八九不离十了;
好了既然构造函数分析的差不多了,根据流程下一步应该是通过messenger.getBinder()方法取出一个Ibinder对象通过Service来返回给Activity。那么接下来再来看看messenger.getBinder()方法:
public IBinder getBinder() {
return mTarget.asBinder();
}
很清楚,调用了IInterface(mTarget就是IMessenger.Stub的实例继承了IMessenger,而IMessenger继承了IInterface)的asBInder方法返回了一个Binder(这里简单的理解其实就是返回了它自己,因为stub内部类也继承了Binder)。
service这边的代码只有这些,那根据上面的使用方法,继续来看Activity这边的代码吧。
Activity这边也有一个构造函数,参数是一个IBinder对象,这个构造函数的源码如下:
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
很清楚返回的其实就是一个proxy类也就是一个binder驱动(不明白的可以看之前的博客)。
然后接下来就是调用了Messenger的send方法;那继续再来看看这个send方法的源码是怎么样的。
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
其实就是调用了刚刚proxy的send方法吧message当参数传进去。这里面的逻辑其实就是通过这个proxy类中的IBinder对象来远程调用service中已经实现的send方法。
一目了然Messenger就是一个典型的aidl的例子。
总结
Server端就是Handler里面实现的MessengerImpl内部类,然后在Service里面被实例化了。而这个aidl也是只有一个方法(send(Message)),就是通过当前Handler来发送一个消息。
Client端就是通过Service返回过来的IBinder类来获取一个proxy对象,通过proxy对象远程调用send方法来完成通讯。
补充:如果要实现Service那边处理完消息返回给activity的话只要在activity里面也创建一个Messenger,然后把这个Messenger通过Message赋值给参数message.replyTo传过去就好了,同样Service就可以通过这个参数里面的Messenger来发送消息给activity通过activity里面handler来处理消息来完成双向通讯。
注意:这里有一点就是如果不是跨进的的话Service和Activity都运行在主线程,那么Service中用于处理消息的Handler里面不能执行耗时的工作,不然会导致ActivityUI界面卡住,因为Handler是创建在Service线程(主线程)用的是主线程的Looper。如果是跨进程的话Activity这边的主线程就不会卡住(Service所在的线程会卡住)。因为在普通的aidl在proxy调用的时候(其实就是调用IBinder.transact方法时)会挂起当前线程因此在Service端执行耗时操作时activity的UI线程会卡住。而messenger和普通的aidl不同之处在于它又添加了一个Handler,而这个Handler是运行在Service所在线程(默认为Service所在进程的主线程)而真正的Messenger.send方法只执行了一个Handler的sengmessage方法(这个方法是运行在底层binder的工作线程中,只要在这个线程中不执行耗时操作调用方所在的线程就不会被挂起太久)。因此不会卡住(Service线程可能会卡住)。这一点我感觉IntentService的实现非常的相似。
Android进阶笔记:Messenger源码详解的更多相关文章
- Android LayoutInflater和findViewById 源码详解
LayoutInflater大家很熟悉,简单点说就是布局文件XML解析器,setContentView函数也是调用了LayoutInflater 用法: View view = LayoutInfla ...
- [转]Linux内核源码详解--iostat
Linux内核源码详解——命令篇之iostat 转自:http://www.cnblogs.com/york-hust/p/4846497.html 本文主要分析了Linux的iostat命令的源码, ...
- Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解
Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解 今天主要理一下StreamingContext的启动过程,其中最为重要的就是Jo ...
- spring事务详解(三)源码详解
系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...
- 条件随机场之CRF++源码详解-预测
这篇文章主要讲解CRF++实现预测的过程,预测的算法以及代码实现相对来说比较简单,所以这篇文章理解起来也会比上一篇条件随机场训练的内容要容易. 预测 上一篇条件随机场训练的源码详解中,有一个地方并没有 ...
- saltstack源码详解一
目录 初识源码流程 入口 1.grains.items 2.pillar.items 2/3: 是否可以用python脚本实现 总结pillar源码分析: @(python之路)[saltstack源 ...
- Shiro 登录认证源码详解
Shiro 登录认证源码详解 Apache Shiro 是一个强大且灵活的 Java 开源安全框架,拥有登录认证.授权管理.企业级会话管理和加密等功能,相比 Spring Security 来说要更加 ...
- udhcp源码详解(五) 之DHCP包--options字段
中间有很长一段时间没有更新udhcp源码详解的博客,主要是源码里的函数太多,不知道要不要一个一个讲下去,要知道讲DHCP的实现理论的话一篇博文也就可以大致的讲完,但实现的源码却要关心很多的问题,比如说 ...
- Activiti架构分析及源码详解
目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...
随机推荐
- IoT之车联网
一. 背景 这是一个笔者在实习公司策划的关于车联网的小项目,也是笔者参加某竞赛的作品<基于云平台的车内滞留儿童状况监测与处理>,本项目旨在为因各种原因导致儿童滞留车内热死.闷死的社会性事件 ...
- vue知识点(1)
处理用户输入 v-on指令添加一个事件监听器 div id="app-5"> <p>{{ message }}</p> <button v-on ...
- Binary Tree Preorder Traversal——经典算法的迭代求解(前序,中序,后序都在这里了)
先序遍历,用递归来做,简单的不能再简单了.代码如下: (以下仅实现了先序遍历,中序遍历类似,后序遍历和这两个思路不一样,具体详见Binary Tree Postorder Traversal) /** ...
- Largest Number——STL的深层理解
Given a list of non negative integers, arrange them such that they form the largest number. For exam ...
- SecureCrt的操持连接办法
保持连接: options -> global options -> General -> Default Session,点击Edit default settings按钮,在Te ...
- ceph在扩展mon节点时,要注意的问题
我开始也是一步一步的按官方文档操作. 但后来还是遇到了问题. 当我要扩展mon节点时,死活出错. (我就一共用了三个节点ceph-admin, ceph-node1, ceph-node2) 比如: ...
- 【JBPM4】EL表达式的使用,实现JAVA与JPDL的交互
user.java实体类 private String kezhang; private String zhuren; /...完善set get 方法.../ 创建流程实例 //创建流程引擎 Pro ...
- python webpy 框架环境架设
前几年使用过 webpy做个些小东西,今天有个东西从拾webpy.但是基本上都忘记了,还是那句古话“好记性不如烂笔头”.这里把相应的步骤梳理下. 前提: 操作系统 windows 一.webpy 方面 ...
- react native android 应用状态(前端或后台)的判断
当Android应用程序被暂时放到了后台,或者又重新回到前台,是否有相应的事件可以处理到? 例如,当你的应用暂时放到了后台,是否应该做出一些操作,暂时保存界面上的数据? 可以参考:https://gi ...
- 原来Notepad++也有列模式(转)
引子 一直在用Notepad++,小巧.顺手.偶尔使用UltraEdit来处理列模式:UE越来越大,启动时间太长,早都烦了.今天上网,偶然间看到,Notepad++也有列模式.拜拜UE,彻底删除你. ...