Android aidl及binder基础知识巩固
作者:义华
1、什么是binder
binder是android framework提供的,用于跨进程方法调用的机制,具有安全高效等特点。
我们知道,在 Android 系统中,每个应用程序都运行在一个独立的进程中,各个进程之间需要进行数据交换和调用,以实现各种功能。为了实现这个目的,binder应运而生。
2、binder的历史
android基于linux内核,linux提供了非常多的跨进程机制。 为什么android要使用binder而不使用linux已有的ipc机制呢,这个抉择在android内部团队中有过争论。 部分工程师认为binder内部的线程等待带来了系统开销,同时没有显示出更好的用处。 但最后binder还是凭借其优势胜出了。 其实,binder是部分android工程师在PalmSource工作时开源的一种ipc机制,负责开发ipc的工程师直接在此binder上进行了移植和开发。
在《Android传奇》一书中有介绍binder的相关历史。
3、binder基本使用
binder是一种架构,这种架构提供了服务端接口、binder驱动和客户端三个模块。
客户端通过binder远程调用服务端接口,binder驱动负责完成这次远程调用。 在android framework中提供了Binder类,一个类如果扩展Binder类,那么该类就有提供远程服务的能力,该类对象一旦创建,其内部就会创建一个隐藏的线程,用来接收binder驱动发送的消息,从而调用Binder类的onTransact()方法。
binder驱动从表现上看,就是在客户端和服务端传递各种消息,以完成跨进程调用。 在任意一个服务端binder对象创建时,同时也会在binder驱动中创建一个对应的mRemote对象,该对象也是binder类型。 客户端就是通过mRemote对象来访问远程服务的,相当于经过了一层代理。 这种机制提供了更好的安全性。
客户端通过获取远程服务在binder驱动中对应的mRemote对象,通过调用mRemote.transact方法从而实现对远程服务的调用。
从整个架构上看,就是服务端提供binder,binder驱动负责转发消息,客户端获取binder引用并调用。 粗略看很简单,细节是魔鬼。
摘抄一个书中的例子,从demo理解binder的调用过程。 比如要提供一个音乐播放的远程调用, 可以提供一个MusicPlayerService,代码如下:
public class MusicPlayerService extends Binder {public void startPlay(String filePath) {}public void stop() {}@Overridepublic void onTransact(int code, Parcel data, Parcel reply, int flag) {// 这个方法很重要,由binder驱动调用// 根据约定的code值分别调用不同的业务方法,此处是startPlay和stopif (code == 0x100) {this.startPlay(file_path)} else if (code == 0x101) {this.stop()}}
}
如上MusicPlayerService就可以提供了远程调用服务了。 但是客户端如何使用呢。通过binder架构知道,要想远程调用,必须获取远程服务在binder驱动中对应的binder代理对象。此处假设已经获取到相关的引用mRemote,那么客户端便可以如下调用。
IBinder mRemote = null;
String filePath = "xxx";
int code = 0x100;Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain(); // 接收远程调用的结果
data.writeString(filePath);
mRemote.transact(code, data, reply, 0)
如上便完成了客户端对服务端的远程调用。 从这个demo就可以看出跨进程调用其实就是要通过Binder驱动来中转一下调用。 以前没看过这个示例时老是不理解aidl生成的规则,不断的复习也总是记不住规则。但是通过这个示例一下就明白了这个过程,在理解的基础上再去手动编写binder类就水到渠成了(虽然一般也用不到手动去写binder类)。
现在就只剩如何获取远程服务对应的binder对象了。 为了简化这个过程,android frameworks通过service提供这个能力。 在service的生命周期方法中有一个onBind方法返回IBinder对象,方法签名如下:
override fun onBind(intent: Intent?): IBinder? {
在调用context.bindService方法进行绑定时,要求传入一个ServiceConnection的对象,在服务绑定成功时会回调onServiceConnection方法,一并传回服务端的Binder代理对象。 如下:
Intent it = new Intent(this, xxxService.class);
context.bindService(intent, new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 此处就拿到了远程服务对应的binder了,客户端通过此binder就可以调用远程服务的方法了。 }@Overridepublic void onServiceDisconnected(ComponentName name) {}
}, Service.BIND_AUTO_CREATE);
仔细想想,应用开发好像也只有这么一条路获取服务端的binder。 但是最初我们定义的MusicPlayerService并不是一个真正的service,无法使用bindService方法,故需要改造一下,让MusicPlayerService继承Service类。 如下:
public class MusicPlayerService extends Service {private XXXBinder mBinder;@Overridepublic IBinder onBind(Intent intent) {return mBinder; // 此处返回服务端的binder}public void startPlay(String filePath) {}public void stop() {}// 由于android应用开发上只能通过service进行远程调用,故在service内部类创建binder,方便调用service定义的业务方法。 public xxxBinder extends Binder {@Overridepublic void onTransact(int code, Parcel data, Parcel reply, int flag) {// 这个方法很重要,由binder驱动调用// 根据约定的code值分别调用不同的业务方法,此处是startPlay和stopif (code == 0x100) {startPlay(file_path)} else if (code == 0x101) {stop()}}}
}
由于java不能多继承,继承了service类就不能继承Binder类了。 那么可以在MusicPlayerService类内部构建一个内部类,继承binder,并通过onBind返回binder对象给客户端。 这个代码架构便是使用binder的基本雏形了。但是,在日常开发过程中,为了扩展性和解耦,一般将业务方法通过接口抽象出来。 如下抽象出播放接口。
public interface IPlayer {public void startPlay(String filePath);public void stop();
}
通过上面的代码可以发现,播放的实现放在XXXBinder类或者MusicPlayerService都是可行的。 为了方便binder调用业务方法,尝试用binder实现接口并实现业务。 改造后的代码如下:
public class MusicPlayerService extends Service {private XXXBinder mBinder;@Overridepublic IBinder onBind(Intent intent) {return mBinder; // 此处返回服务端的binder}// binder实现了IPlayer业务接口public xxxBinder extends Binder implemention IPlayer {@Overridepublic void onTransact(int code, Parcel data, Parcel reply, int flag) {// 这个方法很重要,由binder驱动调用// 根据约定的code值分别调用不同的业务方法,此处是startPlay和stopif (code == 0x100) {this.startPlay(file_path)} else if (code == 0x101) {this.stop()}}public void startPlay(String filePath) {Log.i("test", "startPlay");}public void stop() {Log.i("test", "stop");}}
}
至此,使用binder的代码结构就较为清晰了。以上demo一步步的改造,其实就是为了更加深刻的理解binder的使用,并向aidl靠拢,从而更好的理解aidl生成的代码。 从上代码可以看出,使用binder的代码接口基本是固定的,所以android framewrok提供了一个aidl的工具来简化这个过程。
4、aidl的使用
在对应模块上右键选择aidl,会弹出创建aidl的对话框,输入名字as会自动生成对应的aidl文件。 在aidl文件中加入自己的业务接口即可。 build一下as就会根据aidl自动生成相应的接口类和binder类,具体文件此处省略。

需要注意的是,aidl中只支持Parcelable对象和原子类,如果有自定义的类需要跨进程访问时需要实现Parcelable接口。
客户端通过如下代码获取接口对象并实现调用。
class MusicClient {private var mIMusicPlayer: IMusicPlayer? = nullprivate var mServiceConnection = object: ServiceConnection {override fun onServiceConnected(name: ComponentName?, service: IBinder?) {// 这个方法中提供了是本地通信还是跨进程通信的判断// 这里就客户端进程就获取了远程服务的binder引用了。 aidl工具生成了Stub及Proxy类封装了直接通过binder.transact调用的逻辑,让开发者只需和接口交互即可。 不必关心binder交互的细节。 mIMusicPlayer = IMusicPlayer.Stub.asInterface(service)}override fun onServiceDisconnected(name: ComponentName?) {}}fun bindMusicService(context: Context) {val intent = Intent(context, PlayerService::class.java)context.bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE)}
}
注意在使用aidl时,如果客户端和服务端在不同的工程时,两端的aidl文件要是一样的。实际中可以直接拷贝。
5、binder架构
此处摘抄一张binder架构图

从这张图就可以清晰看到客户端、驱动及服务端的职责,也能更好的理解binder的交互过程。这么看下来,感觉binder驱动其实就是负责对消息进行了转发,同时对交互的过程进行了一定控制。
6、总结
1.一个类要想序列化就要实现Serializable或Parcelable接口,同理一个类要想提供跨进程服务,就必须继承binder类。 binder就像一个标记类一样,只要继承了,就有资格在进程间通信了。
2.一个binder跨进程的通信包含了客户端、服务端和binder驱动三方。
3.在应用开发中主要通过aidl工具、Service及Context.bindService实现跨进程访问。
4.aidl是一个命令行工具,协助生成跨进程调用的样板代码。
想了解Framework底层知识点的小伙伴可以参考一下,在学习过程中我也查阅和收集了一堆的参考学习文档,比如有Handler、Binder、AMS、WMS、PMS、事件分发机制、UI绘制……等等,为了便于自己查阅,将其知识点整合在一起并命名为了《Android Framework 核心学习手册》:
https://qr18.cn/AQpN4J
《Framework 核心知识点汇总手册》:https://qr18.cn/AQpN4J
Handler 机制实现原理部分:
1.宏观理论分析与Message源码分析
2.MessageQueue的源码分析
3.Looper的源码分析
4.handler的源码分析
5.总结

Binder 原理:
1.学习Binder前必须要了解的知识点
2.ServiceManager中的Binder机制
3.系统服务的注册过程
4.ServiceManager的启动过程
5.系统服务的获取过程
6.Java Binder的初始化
7.Java Binder中系统服务的注册过程

Zygote :
- Android系统的启动过程及Zygote的启动过程
- 应用进程的启动过程

AMS源码分析 :
- Activity生命周期管理
- onActivityResult执行过程
- AMS中Activity栈管理详解

深入PMS源码:
1.PMS的启动过程和执行流程
2.APK的安装和卸载源码分析
3.PMS中intent-filter的匹配架构

WMS:
1.WMS的诞生
2.WMS的重要成员和Window的添加过程
3.Window的删除过程

《Android Framework学习手册》:https://qr18.cn/AQpN4J
- 开机Init 进程
- 开机启动 Zygote 进程
- 开机启动 SystemServer 进程
- Binder 驱动
- AMS 的启动过程
- PMS 的启动过程
- Launcher 的启动过程
- Android 四大组件
- Android 系统服务 - Input 事件的分发过程
- Android 底层渲染 - 屏幕刷新机制源码分析
- Android 源码分析实战

相关文章:
Android aidl及binder基础知识巩固
作者:义华 1、什么是binder binder是android framework提供的,用于跨进程方法调用的机制,具有安全高效等特点。 我们知道,在 Android 系统中,每个应用程序都运行在一个独立的进程中,各个进程之间需要进行…...
[日记]LeetCode算法·二十五——二叉树⑤ AVL树(插入+删除)附代码实现
本章的代码实现基于上一篇BST与优先队列的基类进行平衡二叉树,即AVL树。 文章目录 AVL的概念AVL查询效率AVL的插入1.插入节点2.更新平衡因子BF3.旋转调整树的结构3.1 LL 右旋3.2 RR 左旋3.3 LR 左右双旋3.4 RL 右左双旋 4 插入总结 AVL的删除1.寻找删除节点2.更新平…...
flink-1.13.6 例子
-------------------------------------------------------------- flink版本: flink-1.13.6 [rootmaster bin]# pip3 list | grep flink WARNING: Ignoring invalid distribution -andas (/usr/local/python38/lib/python3.8/site-packages) apache-flink 1.13.0 a…...
Go语音基于zap的日志封装
zap日志封装 Zap是一个高性能、结构化日志库,专为Go语言设计。它由Uber开源,并且在Go社区中非常受欢迎。它的设计目标是提供一个简单易用、高效稳定、灵活可扩展的日志系统。 以下是Zap的一些主要特点: 1.高性能:Zap的性能非常出…...
可持续能源技术具有改变世界的潜力,并且已经在多个方面展现出积极的影响。
可持续能源技术的发展在当今全球面临的气候变化和能源安全挑战中扮演着至关重要的角色。我认为可持续能源技术具有改变世界的潜力,并且已经在多个方面展现出积极的影响。以下是我对此的观点: 1,可持续能源技术有助于减少对化石燃料的依赖 化…...
Java常用工具之StringUtils类
目录 一、字符串判空二、分隔字符串三、判断是否为纯数字四、将集合拼接成字符串五、其他方法 字符串(String)在我们的日常工作中,用得非常非常非常多。 在我们的代码中经常需要对字符串判空,截取字符串、转换大小写、分隔字符串、…...
MyBatis-plus的批量插入方式对比分析
MyBatis-plus的批量插入方式对比分析 【摘要】Mybatis批量插入一直是开发者重点关注的问题,本文列举了Mybatis的五种插入方式进行对比分析,验证了五种批量插入的方式的优先级。 1 准备工作 1.1 新建spring项目 略。 1.2 导入pom.xml依赖 <depende…...
【系分论文】论软件开发模型及应用
目录 论题论题介绍论文要点理论素材准备范文摘要正文 论文补充知识 论题 论软件开发模型及应用 论题介绍 软件开发模型( Software Development Model)是指软件开发全部过程、活动和任务的结构框架。软件开发过程包括需求、设计、编码和测试等阶段&…...
渗透测试--5.3.使用john破解密码
前言 由于Linux是Internet最流行的服务器操作系统,因此它的安全性备受关注。这种安全主要靠口令实现。 Linux使用一个单向函数crypt()来加密用户口令。单向函数crypt()从数学原理上保证了从加密的密文得到加密前的明…...
Go中的变量类型
Go中的变量类型 1.为什么要使用变量 变量其实指定的是一段内存地址,根据这个内存地址可以找到我们需要找到的东西。 2.变量类型 变量的功能就是用来存储数据的,根据不同的数据类型可以存储不同的数据。常见的变量的类型 整型、浮点型、布尔型等。变…...
基于STM32的NRF24L01 2.4G通讯模块的驱动实验(HAL库)
前言:本文为手把手教学NRF24L01 2.4G通讯模块的驱动实验,本教程的 MCU 采用STM32F103ZET6与STM32F103C8T6,彼此进行互相通讯。通过 CubeMX 软件配置 SPI 协议驱动NRF24L01 2.4G通讯模块(HAL库)。NRF24L01 2.4G是嵌入式…...
DJ5-3 多路访问链路和协议
目录 一、网络链路 二、广播信道要解决问题 三、多路访问协议 1、基本介绍 2、多路访问协议的类型(3) 四、信道划分协议 1、时分多路访问 TDMA 2、频分多路访问 FDMA 3、码分多路访问 CDMA(略) 五、随机访问协议 1、纯…...
技术领导力?
作品集(Portfolio)会比简历(Resume)更有参考意义。 怎么才算有技术领导力? 1) 能够发现问题,并能够提供解决问题的思路和方案,并能比较方案的优缺点。 2) 能用更简洁有效的方式解决问题。 3…...
计算机的基本工作原理
参考资料: L-1.6: Common Bus system| How basic computer works - YouTube 准备好内存单元、不同类型的寄存器,内存和寄存器、寄存器和寄存器之间都是通过总线连接(假设是直接把数据总线、控制总线、地址总线变成一条总线)。 使用多路复用器实现的总线&…...
【论文简述】Cross-Attentional Flow Transformer for Robust Optical Flow(CVPR 2022)
一、论文简述 1. 第一作者:Xiuchao Sui、Shaohua Li 2. 发表年份:2021 3. 发表期刊:arxiv 4. 关键词:光流、Transformer、自注意力、交叉注意力、相关体 5. 探索动机:由于卷积的局部性和刚性权重,有限…...
【JAVA】Java中方法的使用,理解方法重载和递归
目录 1.方法的概念及使用 1.1什么是方法 1.2方法的定义 1.3方法调用的执行过程 1.4实参和形参 2.方法重载 2.1为什么需要使用方法重载 2.2什么是方法重载 3.递归 3.1什么是递归 3.2递归执行的过程 3.3递归的使用 1.方法的概念及使用 1.1什么是方法 方法就是一个代…...
高级网络计算模式复习
P2P 对等网络(Peer-to-Peer Networks)是分布式系统和计算机网络相结合的产物,在应用领域和学术界获得了广泛的重视和成功,被称为“改变Internet的一代网络技术”。 peer指网络结点,在行为上是自由的——任意加入、退…...
【笔试强训选择题】Day15.习题(错题)解析
作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!! 文章目录 前言 一、…...
图论专题(一)
图论专题(一) 参考文献 BFS和DFS的直观解释 https://blog.csdn.net/c406495762/article/details/117307841Leetcode岛屿问题系列分析 https://blog.csdn.net/qq_39144436/article/details/124173504多源广度优先 https://blog.csdn.net/peko1/article/details/121989497拓扑排…...
新星计划2023【网络应用领域基础】————————Day4
常见的网络基础介绍 前言 我们学习了一些基础的网络协议,以及子网掩码和vlan,同时也做了个简单的单臂路由实验 这篇文章我将仔细的讲解单臂路由的应用和交换机二层接口类型,以及wireshark的教程。 一,交换机二层接口 交换机的二…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
