技术标签: Binder 通信 android 跨进程通信 Android:四大组件 多进程
IPC
),那么你对Binder
一定不陌生Binder
的文章,可是存在一些问题:浅显的讨论Binder
机制 或 一味讲解 Binder
源码、逻辑不清楚,最终导致的是读者们还是无法形成一个完整的Binder
概念Binder
,即:
Binder
跨进程通信机制的模型Binder
在 Android
中的具体实现从而全方位地介绍 Binder
,希望你们会喜欢。
请尽量在PC端而不要在移动端看,否则图片可能看不清。
中文即 粘合剂,意思为粘合了两个不同的进程
网上有很多对Binder
的定义,但都说不清楚:Binder
是跨进程通信方式、它实现了IBinder
接口,是连接 ServiceManager
的桥梁blabla,估计大家都看晕了,没法很好的理解
我认为:对于Binder
的定义,在不同场景下其定义不同
在本文的讲解中,按照 大角度 -> 小角度 去分析Binder
,即:
Binder
跨进程通信机制的模型其中,会详细分析模型组成中的
Binder
驱动
Binder
在 Android
中的具体实现从而全方位地介绍 Binder
,希望你们会喜欢。
在讲解Binder
前,我们先了解一些Linux
的基础知识
Kernel
),即把进程内 用户 & 内核 隔离开来所有进程共用1个内核空间
- copy_from_user():将用户空间的数据拷贝到内核空间
- copy_to_user():将内核空间的数据拷贝到用户空间
进程隔离
为了保证 安全性 & 独立性,一个进程 不能直接操作或者访问另一个进程,即Android
的进程是相互独立、隔离的
跨进程通信( IPC
)
即进程间需进行数据交互、通信
跨进程通信的基本原理
a. 而
Binder
的作用则是:连接 两个进程,实现了mmap()系统调用,主要负责 创建数据接收的缓存空间 & 管理数据接收缓存
b. 注:传统的跨进程通信需拷贝数据2次,但Binder
机制只需1次,主要是使用到了内存映射,具体下面会详细说明
具体请看文章:操作系统:图文详解 内存映射
Binder
跨进程通信机制 模型 基于 Client - Server
模式
此处重点讲解 Binder
驱动的作用 & 原理:
关于其核心原理:内存映射,具体请看文章:操作系统:图文详解 内存映射
Client
进程、Server
进程 & Service Manager
进程之间的交互 都必须通过Binder
驱动(使用 open
和 ioctl
文件操作函数),而非直接交互原因:
Client
进程、Server
进程 & Service Manager
进程属于进程空间的用户空间,不可进行进程间交互Binder
驱动 属于 进程空间的 内核空间,可进行进程间 & 进程内交互所以,原理图可表示为以下:
虚线表示并非直接交互
Binder
驱动 & Service Manager
进程 属于 Android
基础架构(即系统已经实现好了);而Client
进程 和 Server
进程 属于Android
应用层(需要开发者自己实现)所以,在进行跨进程通信时,开发者只需自定义Client
& Server
进程 并 显式使用上述3个步骤,最终借助 Android
的基本架构功能就可完成进程间通信
Server
进程会创建很多线程来处理Binder
请求Binder
模型的线程管理 采用Binder
驱动的线程池,并由Binder
驱动自身进行管理而不是由
Server
进程来管理的
Binder
线程数默认最大是16,超过的请求会被阻塞等待空闲的Binder线程。所以,在进程间通信时处理并发问题时,如使用
ContentProvider
时,它的CRUD
(创建、检索、更新和删除)方法只能同时有16个线程同时工作
Binder
跨进程通信机制 模型 已经有了一个非常清晰的定性认识Binder
跨进程通信机制 模型在 Android
中的具体代码实现方式即分析 上述步骤在
Android
中具体是用代码如何实现的
Binder
机制在 Android
中的实现主要依靠 Binder
类,其实现了IBinder
接口下面会详细说明
Client
进程 需要调用 Server
进程的加法函数(将整数a和b相加)即:
Client
进程 需要传两个整数给Server
进程Server
进程 需要把相加后的结果 返回给Client
进程
Binder
跨进程通信机制 模型的步骤进行分析Server
进程 通过Binder
驱动 向 Service Manager
进程 注册服务Server
进程 创建 一个 Binder
对象
Binder
实体是Server
进程 在Binder
驱动中的存在形式- 该对象保存
Server
和ServiceManager
的信息(保存在内核空间中)Binder
驱动通过 内核空间的Binder
实体 找到用户空间的Server
对象
Binder binder = new Stub();
// 步骤1:创建Binder对象 ->>分析1
// 步骤2:创建 IInterface 接口类 的匿名类
// 创建前,需要预先定义 继承了IInterface 接口的接口 -->分析3
IInterface plus = new IPlus(){
// 确定Client进程需要调用的方法
public int add(int a,int b) {
return a+b;
}
// 实现IInterface接口中唯一的方法
public IBinder asBinder(){
return null ;
}
};
// 步骤3
binder.attachInterface(plus,"add two int");
// 1. 将(add two int,plus)作为(key,value)对存入到Binder对象中的一个Map<String,IInterface>对象中
// 2. 之后,Binder对象 可根据add two int通过queryLocalIInterface()获得对应IInterface对象(即plus)的引用,可依靠该引用完成对请求方法的调用
// 分析完毕,跳出
<-- 分析1:Stub类 -->
public class Stub extends Binder {
// 继承自Binder类 ->>分析2
// 复写onTransact()
@Override
boolean onTransact(int code, Parcel data, Parcel reply, int flags){
// 具体逻辑等到步骤3再具体讲解,此处先跳过
switch (code) {
case Stub.add: {
data.enforceInterface("add two int");
int arg0 = data.readInt();
int arg1 = data.readInt();
int result = this.queryLocalIInterface("add two int") .add( arg0, arg1);
reply.writeInt(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// 回到上面的步骤1,继续看步骤2
<-- 分析2:Binder 类 -->
public class Binder implement IBinder{
// Binder机制在Android中的实现主要依靠的是Binder类,其实现了IBinder接口
// IBinder接口:定义了远程操作对象的基本接口,代表了一种跨进程传输的能力
// 系统会为每个实现了IBinder接口的对象提供跨进程传输能力
// 即Binder类对象具备了跨进程传输的能力
void attachInterface(IInterface plus, String descriptor);
// 作用:
// 1. 将(descriptor,plus)作为(key,value)对存入到Binder对象中的一个Map<String,IInterface>对象中
// 2. 之后,Binder对象 可根据descriptor通过queryLocalIInterface()获得对应IInterface对象(即plus)的引用,可依靠该引用完成对请求方法的调用
IInterface queryLocalInterface(Stringdescriptor) ;
// 作用:根据 参数 descriptor 查找相应的IInterface对象(即plus引用)
boolean onTransact(int code, Parcel data, Parcel reply, int flags);
// 定义:继承自IBinder接口的
// 作用:执行Client进程所请求的目标方法(子类需要复写)
// 参数说明:
// code:Client进程请求方法标识符。即Server进程根据该标识确定所请求的目标方法
// data:目标方法的参数。(Client进程传进来的,此处就是整数a和b)
// reply:目标方法执行后的结果(返回给Client进程)
// 注:运行在Server进程的Binder线程池中;当Client进程发起远程请求时,远程请求会要求系统底层执行回调该方法
final class BinderProxy implements IBinder {
// 即Server进程创建的Binder对象的代理对象类
// 该类属于Binder的内部类
}
// 回到分析1原处
}
<-- 分析3:IInterface接口实现类 -->
public interface IPlus extends IInterface {
// 继承自IInterface接口->>分析4
// 定义需要实现的接口方法,即Client进程需要调用的方法
public int add(int a,int b);
// 返回步骤2
}
<-- 分析4:IInterface接口类 -->
// 进程间通信定义的通用接口
// 通过定义接口,然后再服务端实现接口、客户端调用接口,就可实现跨进程通信。
public interface IInterface
{
// 只有一个方法:返回当前接口关联的 Binder 对象。
public IBinder asBinder();
}
// 回到分析3原处
注册服务后,Binder
驱动持有 Server
进程创建的Binder
实体
Client
进程 使用 某个 service
前(此处是 相加函数),须 通过Binder
驱动 向 ServiceManager
进程 获取相应的Service
信息此时,Client
进程与 Server
进程已经建立了连接
Client
进程 根据获取到的 Service
信息(Binder
代理对象),通过Binder
驱动 建立与 该Service
所在Server
进程通信的链路,并开始使用服务
过程描述
Client
进程 将参数(整数a和b)发送到Server
进程Server
进程 根据Client
进程要求调用 目标方法(即加法函数)Server
进程 将目标方法的结果(即加法后的结果)返回给Client
进程代码实现过程
步骤1: Client
进程 将参数(整数a和b)发送到Server
进程
// 1. Client进程 将需要传送的数据写入到Parcel对象中
// data = 数据 = 目标方法的参数(Client进程传进来的,此处就是整数a和b) + IInterface接口对象的标识符descriptor
android.os.Parcel data = android.os.Parcel.obtain();
data.writeInt(a);
data.writeInt(b);
data.writeInterfaceToken("add two int");;
// 方法对象标识符让Server进程在Binder对象中根据"add two int"通过queryLocalIInterface()查找相应的IInterface对象(即Server创建的plus),Client进程需要调用的相加方法就在该对象中
android.os.Parcel reply = android.os.Parcel.obtain();
// reply:目标方法执行后的结果(此处是相加后的结果)
// 2. 通过 调用代理对象的transact() 将 上述数据发送到Binder驱动
binderproxy.transact(Stub.add, data, reply, 0)
// 参数说明:
// 1. Stub.add:目标方法的标识符(Client进程 和 Server进程 自身约定,可为任意)
// 2. data :上述的Parcel对象
// 3. reply:返回结果
// 0:可不管
// 注:在发送数据后,Client进程的该线程会暂时被挂起
// 所以,若Server进程执行的耗时操作,请不要使用主线程,以防止ANR
// 3. Binder驱动根据 代理对象 找到对应的真身Binder对象所在的Server 进程(系统自动执行)
// 4. Binder驱动把 数据 发送到Server 进程中,并通知Server 进程执行解包(系统自动执行)
步骤2:Server
进程根据Client
进要求 调用 目标方法(即加法函数)
// 1. 收到Binder驱动通知后,Server 进程通过回调Binder对象onTransact()进行数据解包 & 调用目标方法
public class Stub extends Binder {
// 复写onTransact()
@Override
boolean onTransact(int code, Parcel data, Parcel reply, int flags){
// code即在transact()中约定的目标方法的标识符
switch (code) {
case Stub.add: {
// a. 解包Parcel中的数据
data.enforceInterface("add two int");
// a1. 解析目标方法对象的标识符
int arg0 = data.readInt();
int arg1 = data.readInt();
// a2. 获得目标方法的参数
// b. 根据"add two int"通过queryLocalIInterface()获取相应的IInterface对象(即Server创建的plus)的引用,通过该对象引用调用方法
int result = this.queryLocalIInterface("add two int") .add( arg0, arg1);
// c. 将计算结果写入到reply
reply.writeInt(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
// 2. 将结算结果返回 到Binder驱动
步骤3:Server
进程 将目标方法的结果(即加法后的结果)返回给Client
进程
// 1. Binder驱动根据 代理对象 沿原路 将结果返回 并通知Client进程获取返回结果
// 2. 通过代理对象 接收结果(之前被挂起的线程被唤醒)
binderproxy.transact(Stub.ADD, data, reply, 0);
reply.readException();;
result = reply.readInt();
}
}
对比 Linux
(Android
基于Linux
)上的其他进程通信方式(管道、消息队列、共享内存、
信号量、Socket
),Binder
机制的优点有:
Binder
机制 ,总结如下:特别地,对于从模型结构组成的Binder驱动来说:
Binder
模型的原理步骤 & 源码分析Carson带你学Android 文章系列:
Carson带你学Android:页面活动-Activity
Carson带你学Android:广播-BroadcastReceiver
Carson带你学Android:服务-Service
Carson带你学Android:内存承载器-ContentProvider
博客链接:https://carsonho.blog.csdn.net/
文章浏览阅读3k次。刚接触flink没多久,做的一个flink流处理任务,状况百出,下面聊一聊关于数据库操作出的状况。 需求:需要从数据库取一些判断条件,流数据根据判断条件做一些变换(map),所以决定直接在map里操作数据库 1.最初版(调试前):第一反应,操作数据库,上连接池,所以在main里面直接建了一..._flink sql map 函数
文章浏览阅读611次。https://support.huaweicloud.com/ecs_faq/ecs_faq_1005.html按照官方文档修改后,完美秒安装,注意下图中的配置文件名要和你的系统版本号匹配上喔。表1Ubuntu版本号与版本名称对应关系 版本号 版本名称 apt源配置文件 Ubuntu 18.04 (Bionic Beaver) sources.list.bionic..._ubuntu连接华为云的源 很慢
文章浏览阅读660次。通过迭代器来遍历HashMap,演示一下迭代器Iterator的使用Map<Integer, String> map = new HashMap<>();map.put(1, "java");map.put(2, "c++");map.put(3, "php");Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();while (iterator.hasN_hashmap迭代器试用
文章浏览阅读129次。在这篇文章中,我们将讨论Optee中virt_to_phys函数的实现,该函数用于将虚拟地址转换为物理地址。总结一下,Optee中的virt_to_phys函数通过解析页表项和应用ARM64的地址转换规则,实现了将虚拟地址转换为物理地址的功能。Optee中的virt_to_phys函数的作用就是根据当前页表的映射关系,将给定的虚拟地址转换为物理地址。在实际的代码中,还需要处理异常情况和错误检查,以确保函数的正确性和安全性。上述代码是Optee中virt_to_phys函数的简化版本,用于说明其实现逻辑。_virtual to physics函数
文章浏览阅读260次。OulipoTime Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 16577 Accepted Submission(s): 6640Problem DescriptionThe French author _hdu - 1686
文章浏览阅读183次。不知道大家在使用ExecuteNonQuery方法的时候有没有碰到过返回-1的情况。ExecuteNonQuery方法返回增删改时,受影响的行数。那怎么会返回-1呢?查了一些资料后终于得知返回-1的原因。在进行增删改时,数据库能为我们统计受影响行数的前提条件是,关闭NOCOUNT,即 Set nocount off。默认情况下,它是关闭的,即我们是可以得到受影响的行数的。你可以测试如下..._command.executenonquery返回-1
文章浏览阅读870次。我们都说:学习计算机的人到一定岁数,会秃头,这是真的吗?我好害怕。。。其实吧?编程不背这黑锅,学编程的确实工作量大,工作时间长,用脑比较多,但是编程表示:这个黑锅我可担不起,其实吧,导致脱发的原因有很多:遗传性脱发:遗传性脱发也叫先天性脱发,这个是祖传的,只能认命,编程可背不起这个锅。对于男生来说,脱发,啊,好烦,因为一些基因或者激素的原因,更容易脱发。物理或者化学性脱发:物理性:长期接触放射源,长时间玩电脑手机,看电视等原因造成的物理性脱发。哇,这个锅编程能不能甩呢?回答当然是能的,现_编程人会秃头
文章浏览阅读796次。1.论文标题最好要好好琢磨,去除“基于....的”定语后,分析主谓宾后的论文标题没有语法问题;2.论文标题不宜过大,比如“基于.....的手势识别”,但是手势识别的种类多,比如“基于...的手势识别模型”,“基于...的手势识别算法”等等;3.最好从论文的第二章开始讲自己的东西,而不要讲一些已经有的一些定义、定理等,答辩时老师只会关心,论文里你自己做了什么,创新点在哪;4.自己做的部分一定要写清楚..._华科软件学院硕士论文
文章浏览阅读566次。文本分类概述 文本分类是在预定义的分类体系下,根据文本的特征(内容和属性),将给定文本与一个或者多个类别相关联的过程。最终目的是找到一个有效的映射函数,将输入的可视化文本映射为预定义分类体系下的一个或者多个类别。因此,文本分类有两个关键问题:一个是文本的表示,另一个就是分类器的设计。 **|输入文档|—> 预处理 —> **文本表示** —>..._文本分析示意图
文章浏览阅读163次。【代码】vue 连接 MQTT 实现发布/订阅消息。_vue使用mqtt定时器监测消息发送频率
文章浏览阅读1.6k次。在spring+mybatis开发中 遇到插入中文字符数据变成问号的问题1、一般第一步可能会去看spring项目中的web.xml是否设置了字符过滤器但是一看代码已经拷贝过来了啊 ┭┮﹏┭┮2、那会不会是tomcat中没有设置字符的问题?于是打开server.xml空欢喜 还是设置过了啊 ┭┮﹏┭┮3、对了还有个地方可能出问题 那就是数..._mybatis xml select ifnull 中文乱码
文章浏览阅读1.6k次。Qt维基百科,自由的百科全书Qt使用Qt Designer做GUI设计开发者Qt Project、诺基亚、Digia、KDE稳定版本5.0.1[1]/2013年1月31日;41天前预览版本5.0 RC2/2012年12月6日;3个月前编程语言_qt qgr