Android轻量级进程间通信Messenger源码分析
一. 概述
Android中比较有代表性的两大通信机制:1. 线程间Handler通信 2. 进程间Binder通信,本篇文章中我们在理解AIDL原理的基础上来解读一下Messenger的源代码, 并结合示例Demo加深理解。 在看本篇文章前,建议先查阅一下笔者的 Android 进程间通信机制(六) 手写AIDL文件
首先说下我对Messenger的个人理解:
1. 从概念上阐述
Messenger进程间通信的信使,是一个轻量级的IPC通信方案, 和Message消息不是一个概念。
2. 从实现上描述
实现: AIDL + Handler 它的底层实现原理还是使用 AIDL, 对应的文件为(IMessenger.aidl)
相应的接口方法:
oneway interface IMessenger {void send(in Message msg);
}
Messenger使用了Handler进行通信, 调用的这句代码
private final class MessengerImpl extends IMessenger.Stub {public void send(Message msg) {msg.sendingUid = Binder.getCallingUid();//核心代码Handler.this.sendMessage(msg);}}
3. 使用场景
两个进程间只进行简单的,轻量级通信, 不需要处理多线程的业务场景。
二. 示例
2.1 先看一下清单文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.messengertest"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.MessengerTest"><activityandroid:name=".MainActivity"android:exported="true"android:process=":client"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><serviceandroid:name=".MyService"android:exported="true"android:enabled="true"android:process=":server"></service></application></manifest>
我把MainActivity作为客户端, MyService作为服务端, 用android:process标记让两个组件运行在不同的进程中。
2.2 客户端
public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private Messenger mServiceMessenger;//创建客户端 Messenger 对象,并绑定 Handlerprivate Messenger mClientMessenger = new Messenger(new MessengerHandler());private static final class MessengerHandler extends Handler {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);switch (msg.what) {case MyConstants.MSG_FROM_SERVER: //接受来自 服务端 的消息Log.d(TAG, "receive msg from server: " + msg.getData().get("reply"));break;default:break;}}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//绑定服务端的 Service, 成功后用返回的 IBinder 对象创建 MessengerIntent intent = new Intent(this, MyService.class);bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {mServiceMessenger = new Messenger(binder); // 获取服务端的 Messenger对象, 通过它向服务端发送消息Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);Bundle data = new Bundle();data.putString("msg", "hello, this is client");msg.setData(data);msg.replyTo = mClientMessenger ; //将客户端的 Messenger 传给 服务端try {mServiceMessenger.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};
}
2.3 服务端
public class MyService extends Service {private static final String TAG = "MyService";//服务端创建 Messenger对象,并绑定对应 Handler, 用于处理 Client 发过来的消息private final Messenger mServiceMessenger = new Messenger(new MessengerHandler());@Nullable@Overridepublic IBinder onBind(Intent intent) {//返回 Binder 对象给 Clientreturn mServiceMessenger.getBinder();}public MyService() {}private static class MessengerHandler extends Handler {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);switch (msg.what) {case MyConstants.MSG_FROM_CLIENT:Log.d(TAG, "receive msg from client:" + msg.getData().getString("msg"));Messenger clientMessenger = msg.replyTo; //获取传递过来的客户端Messenger对象Message replyMsg = Message.obtain(null, MyConstants.MSG_FROM_SERVER);Bundle bundle = new Bundle();bundle.putString("reply", " server have receive your msg");replyMsg.setData(bundle);try {clientMessenger.send(replyMsg);} catch (RemoteException e) {e.printStackTrace();}break;default:break;}}}
}
2.4 消息常量
public class MyConstants {public static final int MSG_FROM_SERVER = 2;public static final int MSG_FROM_CLIENT = 1;
}
2.5 运行结果
16:12:57.139 31928 31928 D MyService: receive msg from client:hello, this is client
16:12:57.181 31894 31894 D MainActivity: receive msg from server: server have receive your msg
客户端和服务端关键代码处有注释说明,方便理解。
三. 模型
上面Demo,可以用如下图来说明

四. 源码解析
大多数应用,跨进程只是一对一通信, 并且无需执行多线程处理的业务, 此时使用Messenger更适合一点。
我们重点看如下5个方法:
public final class Messenger implements Parcelable {......//1. mTarget 为 IMessenger接口实例化对象private final IMessenger mTarget;//2. 创建一个指向target Handler的Messenger,// 然后调运Messenger的send 实质是调用Handler的sendMessage方法public Messenger(Handler target) {mTarget = target.getIMessenger();}//3. 跨进程发送消息方法public void send(Message message) throws RemoteException {mTarget.send(message);}//4. 获得Messenger的Binder,一般用在service端获取返回public IBinder getBinder() {return mTarget.asBinder();}//5. 获取getBinder相同的Messenger对象,一般用在client端获取public Messenger(IBinder target) {mTarget = IMessenger.Stub.asInterface(target);}......}
第1个方法 mTarget 为 IMessenger 接口实例化对象, 在java语法规则中,接口类不能直接创建对象, 只能实例化实现该接口的类对象,我们来看看IMessenger.java 它是IMessenger.aidl 文件通过aapt编译自动生成的,具体输出路径为:
out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/base/core/java/android/os/IMessenger.java
public interface IMessenger extends android.os.IInterface {......
}
它是一个public 接口类,不能直接通过new IMessenge() 来创建对象,但是可以new 实现了该接口的类对象。

第2和5个方法 是Messenger类的两个构造方法
private final IMessenger mTarget;public Messenger(Handler target) {mTarget = target.getIMessenger();}public Messenger(IBinder target) {mTarget = IMessenger.Stub.asInterface(target);}
可以这样子理解两个方法的用途:
1. 参数为Handler的是远程端进程的实例方法
2. 参数为IBinder的是客户端进程的实例方法
第1个构造方法的内容:
//frameworks/base/core/java/android/os/Handler.java 文件中IMessenger mMessenger;//对于一个Handler对象来说getIMessenger得到的Messenger是一个单例模式对象
final IMessenger getIMessenger() {synchronized (mQueue) {if (mMessenger != null) {return mMessenger;}//这里就是new了一个实现该接口类的对象mMessenger = new MessengerImpl();return mMessenger;}}//这里其实是IMessenger.aidl的接口send的具体实现类private final class MessengerImpl extends IMessenger.Stub {public void send(Message msg) {msg.sendingUid = Binder.getCallingUid();//核心还是与该(进程绑定的Handler)去发送消息Handler.this.sendMessage(msg);}}
结论:Messenger类中的mTarget其实就是一个Handler中 实现了(IMessenger远程IPC send接口)的MessengerImpl 单例对象。
第2个方法的使用,就是在客户端拿到服务端的Messenger对象
在MyService.java中 我们看下Service中的onBind实现,其调运了Messenger的getBinder方法,这个方法源码如下:
//服务端创建 Messenger对象,并绑定对应 Handler, 用于处理 Client 发过来的消息private final Messenger mServiceMessenger = new Messenger(new MessengerHandler());@Nullable@Overridepublic IBinder onBind(Intent intent) {//返回 Binder 对象给 Clientreturn mServiceMessenger .getBinder();}
public IBinder getBinder() {return mTarget.asBinder();}//调用mTarget.asBinder()方法,也即是this对象自己//在 Stub类中public static abstract class Stub extends android.os.Binder implements android.os.IMessenger{....@Override public android.os.IBinder asBinder(){return this;}....}//还记得上面的Handler类中的 MessengerImpl 实现了 IMessenger.StubIMessenger mMessenger = new MessengerImpl()private final class MessengerImpl extends IMessenger.Stub {}
可以看见,其实asBinder返回的就是this, 也就是把Service中的Messenger通过onBind方法返回给客户端。
然后再回到客户端 MainActivity.java 中的 onServiceConnected 方法
private Messenger mServiceMessenger;private final IMessenger mTarget;// 1. 获取服务端的 Messenger对象, 通过它向服务端发送消息public void onServiceConnected(ComponentName name, IBinder binder) {mServiceMessenger = new Messenger(binder); // 2. 上面的 new Messenger(binder)调用方法/*** Create a Messenger from a raw IBinder, which had previously been* retrieved with {@link #getBinder}.* * @param target The IBinder this Messenger should communicate with.*/public Messenger(IBinder target) {mTarget = IMessenger.Stub.asInterface(target);}
通过asInterface方法转化一下,由于不在同一个进程,会把Stub转换成Proxy代理对象
public static android.os.IMessenger asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof android.os.IMessenger))) {return ((android.os.IMessenger)iin);}return new android.os.IMessenger.Stub.Proxy(obj);}
就这样客户端就拿到了服务端的Messenger对象,接下来就可以在客户端中用Messenger给服务端发送消息了。
五. 小结
1. 客户端和服务端都有自己的Handler对象,用于在各自的进程中接收处理消息,发送消息则是和Handler绑定的Messenger对象。
2. 通过IMessenger.aidl 中的 send(Message msg)方法进行跨进程通信, 注意 msg.replyTo变量实则是一个Messenger对象, 用于两个进程传递各自的Messenger对象。
相关文章:
Android轻量级进程间通信Messenger源码分析
一. 概述 Android中比较有代表性的两大通信机制:1. 线程间Handler通信 2. 进程间Binder通信,本篇文章中我们在理解AIDL原理的基础上来解读一下Messenger的源代码, 并结合示例Demo加深理解。 在看本篇文章前,建议先查阅一下笔者的…...
C#开发AGV地图编辑软件
C#自己开发AGV地图编辑软件: 1、自由添加和删除站点、停车位、小车、运行路径。 2、编辑得地图以XML文件保存。 3、导入编辑好地图的XML文件。 4、程序都是源码,可以直接在此基础上进行二次开发。 下载链接:https://download.csdn.net/d…...
嵌入式学习day22 Linux
文件IO: 1. lseek off_t lseek(int fd, off_t offset, int whence); 功能: 重新设定文件描述符的偏移量 参数: fd:文件描述符 offset:偏移量 whence: SEEK_SET 文件开头 …...
不确定性问题的论文笔记
Statistics starting from 01/2024, 仅列出了优秀工作中的一部分 每一年的排列顺序: CVPR, ICLR, ECCV, ICCV, ICML, AAAI, TPAMI,TIP,Arxiv 等 每周更新 2024 论文信息速览笔记是 否 已精读精读笔记Shao W, Xu Y, Peng L, et al. Failure Detection fo…...
C语言推荐书籍
本书详细讲解了C语言的基本概念和编程技巧。全书共17章。第1章、第2章介绍了C语言编程的预备知识。第3章~第15章详细讲解了C语言的相关知识,包括数据类型、格式化输入/输出、运算符、表达式、语句、循环、字符输入和输出、函数、数组和指针、字符和字符串…...
基于uniapp微信小程序的汽车租赁预约系统
随着现代汽车租赁管理的快速发展,可以说汽车租赁管理已经逐渐成为现代汽车租赁管理过程中最为重要的部分之一。但是一直以来我国传统的汽车租赁管理并没有建立一套完善的行之有效的汽车租赁管理系统,传统的汽车租赁管理已经无法适应高速发展,…...
ClickHouse 基础(一)
官网 ClickHouse release 24.1, 2024-01-30 以毫秒为单位查询数十亿行 ClickHouse是用于实时应用和分析的最快、资源效率最高的开源数据库。 安装ClickHouse 使用ClickHouse,你有三个选择: ClickHouse云:官方ClickHouse作为一项服务,-由ClickHouse的创…...
07-k8s中secret资源02-玩转secret
一、回顾secret资源的简单实用 第一步:将想要的数据信息【key:value】中的value值,使用base64编码后,写入secret资源清单中; 第二步:创建secret资源; 第三步:pod资源引用secret资源&…...
HTTP特性
大家好我是苏麟 , 今天说说HTTP特性. 资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 到目前为止,HTTP 常见到版本有 HTTP/1.1,HTTP/2.0,HTTP/3.0,不同版本的 HTTP 特性是不一样的。 这里先用 HTTP/1.1 版本给大家介…...
ARM 之十六 详解 CMSIS 版本变迁、各组件使用示例
目前,CMSIS 已经发展到了第六版,其目录结构也发生了重大的变化。在不断发展中,很多原来 CMSIS 的组件被不断独立出去,并因此成立了很多开源社区,今天就来学习一下! 由于 CMSIS 已经包含了相当丰富的文档,因此,本文重点学习版本之间的变化以及一些实际使用示例。 什么是…...
【北京游戏业:出海竞争实力全面】
本文将深入分析北京的游戏行业发展。在上海、广州、北京、深圳、成都、杭州、福建七大游戏产业中心城市中,北京无疑是出海竞争力最强的游戏产业集群。本文将全面剖析北京游戏行业的发展现状。 北京是中国游戏产业的发源地。拥有从游戏引擎到美术设计等完整的产业链…...
课程大纲:图像处理中的矩阵计算
课程名称:《图像处理中的矩阵计算》 课程简介: 图像处理中的矩阵计算是图像分析与处理的核心部分。本课程旨在教授学员如何应用线性代数中的矩阵计算,以实现各种图像处理技术。我们将通过强调实际应用和实践活动来确保学员能够理解和掌握这些…...
【Go语言】Go语言的数据类型
GO 语言的数据类型 Go 语言内置对以下这些基本数据类型的支持: 布尔类型:bool 整型:int8、byte、int16、int、uint、uintptr 等 浮点类型:float32、float64 复数类型:complex64、complex128 字符串:st…...
2024年2月19日 - mis
当需要在两个或多个子模式间插入文本时,这个特性尤其有用。这里有个脚本,它使用子模式在大数字中插入逗号。 $ echo "1234567" | sed { > :start > s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/ > t start > } 1,234,567 $ 这个脚本将匹…...
【JavaWeb】网上蛋糕商城-项目搭建
学习目标 了解网上蛋糕商城的项目需求 了解网上蛋糕商城的功能结构 熟悉E-R图和数据表的设计 熟悉项目环境的搭建 通过前面章节的学习,相信读者应该已经掌握了Web开发的基础知识,学习这些基础知识就是为开发Web网站奠定基础。如今,电子商…...
【Flink状态管理五】Checkpoint的设计与实现
文章目录 1. Checkpoint的整体设计2. Checkpoint创建源码解析2.1. DefaultExecutionGraphBuilder.buildGraph2.2. ExecutionGraph.enableCheckpointing 由于系统原因导致Flink作业无法正常运行的情况非常多,且很多时候都是无法避免的。对于Flink集群来讲,…...
How to install a specific version of a package in R
How to install a specific version of a package in R 在使用R语言完成数据分析的过程中,很多时候,因为项目实际需要,我们应该指定某些库文件的安装包的版本,这个时候,我们可以基于devtools包中的函数install_version…...
SIGSEGV 段错误
SIGSEGV是在Unix/Linux系统中表示"Segmentation Fault"(分段错误)的信号。当一个进程访问未分配给它的内存、访问超出其内存边界或者访问不允许的内存区域时,就会产生SIGSEGV信号,导致进程被操作系统终止。 SIGSEGV的常…...
OpenCV 4基础篇| OpenCV简介
目录 1. 什么是OpenCV2. OpenCV的发展历程3. 为什么用OpenCV4. OpenCV应用领域5. OpenCV的功能模块5.1 基本模块5.2 扩展模块5.3 常用函数目录 1. 什么是OpenCV OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它…...
Vue常用内置指令,代码Demo演示和讲解
文章目录 一、v-text二、v-html三、v-bind四、v-model五、v-on六、v-show七、v-if、v-else八、v-for九、v-cloak 一、v-text 作用,更改标签的显示值使用 v-text 会读取 data中的变量值如果变量存在替换原来的值,如果不存在则报错 <!DOCTYPE html>…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
