Binder 系列导读:九篇文章应该怎样读?
Binder 难读,不是因为缺一张更大的调用链图,而是因为 Java 方法、协议、远端引用、驱动对象和线程等待经常被压成一句“走 Binder”。这九篇沿同一笔调用逐层拆开:先看它怎样跨进程,再追协议、对象、驱动、服务发现和线程,最后用身份、死亡通知与一次慢调用检验这套模型是否真的能用于工程现场。
不必按函数名背源码。第一次读到能说清每一层“拿到什么、交给谁、何时算完成”即可;遇到具体问题,再回到对应篇目的源码坐标和证据段落。
讨论范围固定为 Framework Java AIDL + /dev/binder。NDK / Rust 后端、hwbinder、vndbinder 与 Stable AIDL 只在需要划边界时出现,不与这条主调用链混写。
一、四条阅读路线
路线 0:15 分钟最短路径
这条路线按小节而不是按整篇阅读,先得到一个可用模型:
读完先能区分 Proxy / Stub、handle / node、服务发现、Binder 线程 / Handler 和同步 / oneway;函数级源码第二遍再追。
路线 A:第一次建立 Binder 模型
按下面顺序阅读每篇的调用图、对象表和主链总结,函数级分支暂时跳过:
一:跨进程调用地图
→ 二:AIDL 协议
→ 三:远端对象引用
→ 四:`BC_TRANSACTION` 到 `BR_TRANSACTION`
→ 五:服务发现
→ 六:线程与完成语义
读完应能回答:
谁把方法编码成事务?
谁代表远端对象?
驱动怎样知道送到哪里?
服务端哪条线程执行?
同步返回、oneway 返回和 Handler 完成分别意味着什么?
路线 B:Android 系统源码下钻
一:Java → JNI → libbinder 总入口
→ 三:BinderProxy / BpBinder / binder_ref / binder_node
→ 四:binder_transaction() 路由与对象转换
→ 五:handle 0 与 servicemanager
→ 六:Binder 线程池、嵌套事务和重入
→ 七:calling identity
→ 八:死亡通知
路线 C:故障定位
六:线程、Handler、重入与死锁
→ 七:身份是否在转线程后丢失
→ 八:远端死亡和重连竞态
→ 九:线程栈、Binder flow、日志与 dumpsys 证据链
二、每篇只拆一层
同一个概念只在最相关的篇目里完整解释;其他篇只引用结论,不重新铺一遍定义。这样顺序读不会反复,单篇查问题也能找到明确入口。
| 篇目 | 这一篇拆什么 | 读完应抓住什么 |
|---|---|---|
| 一 | 一次同步 Java 方法怎样跨进程执行并返回 | 三条边界、两端入口线程、reply |
| 二 | AIDL 怎样生成双方对称的协议胶水 | Proxy、Stub、Parcel、descriptor、code |
| 三 | 本地 BinderProxy 怎样代表远端 Binder 端点 | Java / Native / Kernel 对象关系 |
| 四 | 驱动怎样实际路由和转换一笔事务 | target 语义、队列、buffer、对象转换 |
| 五 | 客户端第一次怎样通过名字获得 Binder 引用 | 注册、查询、handle 0 的 context 范围 |
| 六 | 谁执行事务,同步、oneway、Handler 和重入怎样关联 | 完成语义与并发矩阵 |
| 七 | 服务端怎样读取直接调用者身份 | 直接发送方、线程切换、clear/restore |
| 八 | 远端死亡怎样变成客户端通知 | linkToDeath 竞态、generation、恢复边界 |
| 九 | 一次调用慢或失败时怎样建立可验证 RCA | 不重叠时间片和证据三角形 |
三、贯穿系列的 BinderLab 接口
BinderLab AIDL 目录提供完整可构建协议;正文只摘取每篇需要的方法。延迟、oneway、callback 实验使用 requestId 关联事件,死亡实验另用 connectionEpoch / generationId 区分连接代次。
| 方法 | 第一次承担主线的文章 |
|---|---|
add() | 第一篇:同步调用总图 |
addWithRequestId() | 第九篇:Binder 入口等待 Handler 的可关联延迟实验 |
| AIDL 生成协议 | 第二篇 |
registerCallback() / IResultCallback | 第三篇:异步 callback、角色反转和 Binder 引用传播 |
notifyValue() | 第六篇:Binder 入口内的 oneway 串行 |
getAsyncWorker() / IAsyncWorker | 第六篇:同 node 串行与不同 node 可并发的对照 |
notifyValueViaHandler() | 第六篇:Binder 转 Handler 后的异步 callback |
addAndCallback() / ISyncResultCallback | 第六篇:同步嵌套事务与等待线程复用 |
四、系列统一基线
除非单篇明确标出其他参考,九篇正文和 BinderLab 使用同一组读者口径:
| 维度 | 统一值 |
|---|---|
| Framework / JNI / libbinder / servicemanager | android-16.0.0_r4,Android 16 QPR2 |
| Binder Driver | Android Common Kernel android16-6.12-2025-12_r1,commit 67fe3c9d |
| compile SDK | Android SDK Platform 36.1 |
| targetSdk | 36 |
| minSdk | 26;这是 APK 声明的最低安装 API,证据包不包含 API 26 运行或行为验证 |
| Build Tools / AIDL | 36.0.0 |
| 真机证据 | Android 16 / API 36 |
| BinderLab 发布 | android16-qpr2-evidence-v1.3 |
五、核心术语表
| 术语 | 稳定含义 | 不要理解成 |
|---|---|---|
| AIDL 业务 Proxy | 提供类型化业务方法并按协议写 Parcel | Java BinderProxy |
| BinderProxy | Java 层通用远端 IBinder 代理 | 服务端对象地址 |
| BpBinder | Native 层远端 Binder 代理 | Java 业务接口 |
| handle | 某个引用持有进程查找 binder_ref 的编号 | 全系统唯一对象 ID |
binder_ref | 某个进程对远端 node 的驱动引用 | 远端 Java 对象 |
binder_node | 本地 Binder 端点在驱动中的表示 | 直接持有 Java/C++ 指针的对象 |
| Stub | 接收 code、解码 Parcel 并分发本地实现 | 固定运行在主线程的对象 |
| descriptor | 标识接口协议 | 服务名或 handle |
| transaction code | 标识接口中的方法 | 全局方法 ID |
| ServiceManager | 通过名字发现根 Binder 引用 | 所有业务调用的中转站 |
| handle 0 | 当前 Binder context 的 context manager 入口 | 跨 /dev/binder、hwbinder、vndbinder 的全局唯一对象 |
| 同步事务 | 调用方等待服务端方法返回和 reply | 服务所有内部异步工作都完成 |
| oneway | 调用方不等待业务 reply | 新建线程、并行执行或保证成功 |
| calling UID | 当前事务的直接发送方 UID | 多跳调用自动透传的原始业务发起者 |
| DeathRecipient | 旧远端 Binder 失效通知 | 保活或自动重连 |