Android Binder 机制详解
Binder 是 Android 系统中最核心的进程间通信(IPC)机制。
它解决的核心问题是:
一个进程如何安全、高效地调用另一个进程中的方法,并拿到执行结果?
例如,你在 Android 上通过 UIAutomator2 自动点击屏幕,看似只是执行了一句:
d.click(500, 800)
但 UIAutomator2 所在的进程并不能直接操作 Android 系统的输入服务,它需要通过系统提供的接口完成跨进程调用。
这背后就涉及 Binder IPC。
下面从原理、架构、调用过程、底层实现,以及 UIAutomator2 的实际使用场景来讲解。
1. 为什么 Android 需要 Binder?
Android 中存在大量独立的进程,例如:
进程 A:微信
进程 B:支付宝
进程 C:UIAutomator2
进程 D:system_server(Android 系统服务)
每个进程都有自己的虚拟地址空间。
假设 UIAutomator2 想获取当前屏幕上的 UI 控件信息:
-
UIAutomator2 运行在自己的进程中。
-
系统的无障碍交互能力由其他系统组件提供。
-
UIAutomator2 不能直接读取系统服务的内存。
-
它需要通过跨进程通信请求系统服务提供数据。
Binder 就是 Android 解决这个问题的主要机制。
它不仅能传递数据,还能实现类似普通函数调用的跨进程方法调用,也就是 RPC(Remote Procedure Call,远程过程调用)。
这里的“远程”不一定指另一台机器,也可以是同一台设备上的另一个进程。
2. Binder 的整体架构
Binder 可以分成四个核心部分:

四个核心角色:
| 组件 | 作用 |
|---|---|
| Client | 发起调用的进程 |
| Server | 提供功能的进程 |
| Binder Driver | 内核驱动,负责跨进程传递事务 |
| ServiceManager | 管理 Binder 服务的注册与查询 |
在 Client 中,通常有一个 Proxy(代理对象);在 Server 中,则有对应的 Stub(服务端接收对象)。
这也是 Binder 很重要的一点:让跨进程调用看起来像调用本地方法。
3. 一次 Binder 调用是如何完成的?
假设进程 A 想调用进程 B 的一个方法:
int result = calculator.add(10, 20);
虽然看起来只是一次普通 Java 方法调用,但 calculator 如果是远程接口代理,底层实际要完成以下步骤。

这里涉及几个关键技术。
Parcel(序列化容器)
进程 A 不能直接把内存中的 Java 对象指针交给进程 B,因为这个指针在另一个进程中通常没有意义。
所以需要将参数编码成可传输的格式,例如:
方法编号:1
参数1:10
参数2:20
接收方再按照约定的格式解析参数。
Binder Driver(内核驱动)
Binder 在 Linux 内核中提供驱动,Android 通常通过 /dev/binder 访问它。
用户态 Binder 库通过系统调用,例如 ioctl(),与 Binder 驱动交互,进行事务提交、读取回复等操作。
Binder 线程池
Server 端通常使用 Binder 线程池处理跨进程请求。
因此,如果有多个进程同时调用一个服务,服务端可能有多个 Binder 线程并发执行请求。这也意味着服务端需要考虑线程安全问题。
4. Binder 和 AIDL 是什么关系?
这两个概念很容易混淆。
-
Binder 是底层 IPC 机制。
-
AIDL(Android Interface Definition Language)是定义跨进程接口的语言,并且能够生成 Binder 通信所需的代码。
例如,我们定义一个 AIDL 文件:
// ICalculator.aidl
interface ICalculator {
int add(int a, int b);
}
AIDL 工具会生成支持跨进程调用的接口和相关代码。典型 Java 实现中涉及:
ICalculator
|
├── Stub
| 服务端接收 Binder 调用
|
└── Proxy
客户端发送 Binder 调用
客户端可以像这样调用:
int result = calculator.add(10, 20);
而不必手动处理每个 Binder 事务。
可以把它们理解为:
Binder 负责把请求送过去,AIDL 帮助开发者定义请求是什么以及怎样处理。
5. Binder 有什么特点?
| 特点 | 说明 |
|---|---|
| 高效 | 专门针对 Android 本机进程间通信设计 |
| 安全 | 可基于调用方 UID、PID 和系统安全策略进行权限检查 |
| 易用 | 可封装为类似本地方法的调用 |
| 同步和异步 | 支持同步事务和 oneway 异步事务 |
| 跨语言 | 支持 Java、C++ 等语言环境 |
这里要注意,Binder 不是完全没有通信开销。序列化、线程调度、数据复制等仍然存在。
而且 Binder IPC 本身并不意味着调用方拥有目标服务的全部权限,服务端仍然可以拒绝未授权的操作。
6. 回到 UIAutomator2:Binder 在哪里?
这部分与你前面看到的「UIAutomator2 的全量 IPC」关系最大。
以开源项目 openatx/uiautomator2 为例,可以把操作链路简化为:
电脑
|
| Python: d.click(500, 800)
|
| HTTP / JSON-RPC
v
Android 设备
|
| UIAutomator2 服务
|
| UiAutomator / UiAutomation API
|
| Binder IPC
v
Android 系统服务
|
| 输入事件处理
v
目标 App 接收到点击事件
这里有一个特别重要的区别:
Python 与设备端 UIAutomator2 服务之间的 HTTP/JSON-RPC 通信,不是 Binder。
Binder 主要出现在设备端与 Android 系统服务之间的跨进程交互。实际点击操作可能涉及输入管理相关服务,而 UI 层级信息读取则会涉及无障碍相关框架。具体调用链会随 Android 版本和操作类型变化。
因此,如果有人说:
采集 UIAutomator2 的全量 Binder IPC
技术上可能意味着希望观察其触发的 Binder 事务,例如:
-
调用了哪些系统服务接口。
-
对应的 Binder transaction code 是多少。
-
请求参数和返回数据包含什么。
-
调用来自哪个进程,发往哪个进程。
-
某次 UI 操作触发了多少次 Binder 事务。
但这些只是可能的采集目标。单纯捕获 UIAutomator2 进程的 Binder 调用,并不等于捕获整个系统的全部 IPC,也不一定能完整恢复一次操作的所有间接调用链。
7. 如何在 Android 设备上初步观察 Binder?
可以通过 ADB 查看部分系统服务:
adb shell service list
可以列出系统已注册的 Binder 服务,例如 Activity、Window 等相关服务,具体名称取决于设备和版本。
也可以尝试:
adb shell dumpsys activity
查看 Activity 管理相关的系统状态。
这些命令有助于理解系统服务,但 不等于抓取了 Binder 通信报文。如果要追踪实际事务,还需要 Binder 相关的内核追踪能力、相应权限或调试环境。
最后总结
理解 Binder,可以记住三个层次:
应用层:AIDL / Proxy / Stub
定义接口,让跨进程调用像普通函数调用。
内核层:Binder Driver
在进程之间传递请求、引用和响应。
系统服务层:system_server 等
真正执行窗口管理、输入管理、无障碍交互等操作。
对于 UIAutomator2,最值得进一步研究的是:一次 click() 或 dump_hierarchy() 究竟触发了哪些 Binder 事务?
这能帮助你从调用系统 API 的层面,理解 UIAutomator2 是怎样操作 Android 系统的。