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步:

  1. 定义了一个MessengerHandler的内部类并且实现了handleMessage的内部回调;
  2. 创建了一个Messenger的对象,在Messenger的构造函数中传入刚刚创建的handler实例;
  3. 在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步:

  1. 通过绑定Service来获得IBinder对象;
  2. 通过IBinder对象重新构造一个Messenger;
  3. 通过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的例子。

总结

  1. Server端就是Handler里面实现的MessengerImpl内部类,然后在Service里面被实例化了。而这个aidl也是只有一个方法(send(Message)),就是通过当前Handler来发送一个消息。

  2. 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源码详解的更多相关文章

  1. Android LayoutInflater和findViewById 源码详解

    LayoutInflater大家很熟悉,简单点说就是布局文件XML解析器,setContentView函数也是调用了LayoutInflater 用法: View view = LayoutInfla ...

  2. [转]Linux内核源码详解--iostat

    Linux内核源码详解——命令篇之iostat 转自:http://www.cnblogs.com/york-hust/p/4846497.html 本文主要分析了Linux的iostat命令的源码, ...

  3. Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解

    Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解 今天主要理一下StreamingContext的启动过程,其中最为重要的就是Jo ...

  4. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  5. 条件随机场之CRF++源码详解-预测

    这篇文章主要讲解CRF++实现预测的过程,预测的算法以及代码实现相对来说比较简单,所以这篇文章理解起来也会比上一篇条件随机场训练的内容要容易. 预测 上一篇条件随机场训练的源码详解中,有一个地方并没有 ...

  6. saltstack源码详解一

    目录 初识源码流程 入口 1.grains.items 2.pillar.items 2/3: 是否可以用python脚本实现 总结pillar源码分析: @(python之路)[saltstack源 ...

  7. Shiro 登录认证源码详解

    Shiro 登录认证源码详解 Apache Shiro 是一个强大且灵活的 Java 开源安全框架,拥有登录认证.授权管理.企业级会话管理和加密等功能,相比 Spring Security 来说要更加 ...

  8. udhcp源码详解(五) 之DHCP包--options字段

    中间有很长一段时间没有更新udhcp源码详解的博客,主要是源码里的函数太多,不知道要不要一个一个讲下去,要知道讲DHCP的实现理论的话一篇博文也就可以大致的讲完,但实现的源码却要关心很多的问题,比如说 ...

  9. Activiti架构分析及源码详解

    目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...

随机推荐

  1. android dialog 筛选列表

    1.效果图 2. 布局文件 1)显示筛选的标题 <?xml version="1.0" encoding="utf-8"?> <LinearL ...

  2. Author name disambiguation using a graph model with node splitting and merging based on bibliographic information

    Author name disambiguation using a graph model with node splitting and merging based on bibliographi ...

  3. 18:django 日志系统

    django使用python内建的logging模块去建造自己的系统日志的,如果你想详细了解这个模块的话,请自己去看python的说明文档,这里仅仅介绍django中的日志系统 日志配置包括四个部分: ...

  4. linux命令(17):pwd命令

    1:查看当前工作目录的完整路径命令:pwd 2:目录连接链接时,pwd -P  显示出实际路径,而非使用连接(link)路径:pwd显示的是连接路径: [root@host-172-168-80-55 ...

  5. P1474 货币系统 Money Systems(完全背包求填充方案数)

    题目链接:https://www.luogu.org/problemnew/show/1474 题目大意:有V种货币,求用V种货币凑出面值N有多少种方案. 解题思路:就是完全背包问题,只是将求最大价值 ...

  6. Ubuntu 17.04 搭建LAMP服务器环境流程

    安装Apache2 安装代码 sudo apt-get install apache2 更改默认目录 sudo vim /etc/apache2/apache2.conf // 将 <Direc ...

  7. 使用Nginx的配置对cc攻击进行简单防御

    ddos攻击:分布式拒绝服务攻击,就是利用大量肉鸡或伪造IP,发起大量的服务器请求,最后导致服务器瘫痪的攻击. cc攻击:类似于ddos攻击,不过它的特点是主要是发起大量页面请求,所以流量不大,但是却 ...

  8. linux shell date 用当天时间做备份文件名

    #!/bin/bash #date  显示时间,我们可以用时间的不同做为备份文件的名字,这样以前的备份就不会被覆盖 datename=$(date +%Y%m%d-%H%M%S)           ...

  9. Html导出Pdf

    最近领导给了一个新的需求:给了我一个html页面,让我导出Pdf页面 由于以前没有搞过这方面的需求,所以查百度找资料,找了一大堆的好东西,像ItextSharp和wkhtmltopdf 废话不说,进入 ...

  10. Arduino可穿戴开发入门教程LilyPad和LilyPad Simple的介绍

    Arduino可穿戴开发入门教程LilyPad和LilyPad Simple的介绍 LilyPad和LilyPad Simple的介绍 LilyPad和LilyPad Simple是LilyPad微控 ...