总览:一句话抓住关系
- Binder = Android 的底层 IPC 机制(驱动 + 用户态库 + 服务注册中心)。
- AIDL = 帮你生成基于 Binder 的 Stub/Proxy 代码的接口定义工具(语法糖)。
- IPC = 跨进程通信的总称;Android 的主力方案几乎都“落地在 Binder 上”(AIDL、Messenger、Provider、Broadcast、System Service…)。
1) Binder 深入原理(内核 & 数据面)
1.1 关键组成
- Binder 驱动(
/dev/binder,内核模块):管理传输、对象引用、线程唤醒、缓冲区。 - libbinder(用户态) :提供
IBinder/Parcel等抽象(Java 层和 Native 层各自有实现)。 - ServiceManager(上下文管理器) :系统服务注册/查询中心(
addService/getService)。 - BBinder/BpBinder(C++) | Binder/BinderProxy(Java) :服务端“实体对象”/客户端“代理对象”。
1.2 内核关键结构(常被问)
binder_proc:代表一个进程在驱动中的上下文(包含其映射区、线程池等)。binder_thread:代表进入驱动的每个线程(轮询、收发事务)。binder_node:服务端导出的 Binder 实体(可被他进程引用)。binder_ref:客户端对远端实体的“引用句柄”(handle)。binder_transaction:一次请求/应答的封装。binder_buffer:目标进程的映射区里分配的收发缓冲。
记忆法:proc/thread 管理执行,node/ref 管理对象引用,transaction/buffer 管理数据。
1.3 “一次拷贝”是怎么做到的
- 发送方在用户空间准备好
Parcel数据; - 驱动为接收方进程在它的 mmap 区域里分配
binder_buffer; - 驱动把数据从发送方用户态直接拷到接收方的映射缓冲(一次 copy);
- 接收方用户态直接“看到”这块缓冲里的数据(因为已映射到它的地址空间)。
对比传统 IPC(如 Socket):用户→内核 + 内核→用户 = 两次。
Binder:用户→“对端的映射缓冲”(内核完成一次 copy)= 一次。
1.4 事务时序(含命令流)
- Client
transact()→ 进入驱动(ioctl(BINDER_WRITE_READ))→ 发送BC_TRANSACTION; - 驱动分配目标进程 buffer,复制数据,入队目标
binder_thread; - Server 线程池被唤醒,读到
BR_TRANSACTION,执行 StubonTransact(); - 执行完成→ 写回
BC_REPLY,客户端收到BR_REPLY; - 同步调用在 Client 线程阻塞等待,异步(
FLAG_ONEWAY)不等待。
关键点:Java 层你只看见 transact/onTransact;内核里其实是 BC_* / BR_* 命令来回跑。
1.5 线程池与并发
- 每进程一个 Binder 线程池(上限常见 16,可调),由驱动按需唤醒。
onTransact()在 Binder 线程里执行(不是主线程)。
→ 切忌在onTransact()里做耗时(I/O/网络/锁),否则“卡死”池子。
1.6 安全 & 鉴权
- 驱动在
binder_transaction_data里附带调用方 UID/PID;服务端可用
Binder.getCallingUid()/getCallingPid()校验权限/签名。 - 再叠加 Android 权限系统 + SELinux(system_server 与 app 间)。
1.7 传递大对象与 FD
- Bundle 太大 → 触发
TransactionTooLargeException(常见 ANR 根因)。
规则:每进程 Binder 事务缓冲区约 1MB(含并发事务占用,非“每次都能 1MB”)。 - 大块数据:用 ashmem/共享内存,通过 Binder 仅传 文件描述符(fd) (
BINDER_TYPE_FD)。
1.8 进程死亡与重连
linkToDeath(DeathRecipient)监听对端进程死亡(如服务崩溃),回调binderDied()做重连/降级。
2) AIDL:生成了什么代码,怎么跑
2.1 编译产物(Java)
给定:
aidl
复制编辑
interface IBook {
String name();
oneway void prefetch(int id);
}
编译生成 IBook.java,里面含:
-
public interface IBook extends IInterface -
public static abstract class Stub extends Binder implements IBookonTransact(int code, Parcel data, Parcel reply, int flags):根据 code 分发方法。public static IBook asInterface(IBinder obj):把IBinder包装成 Proxy 或返回本地实现。
-
private static class Proxy implements IBookname():打包到Parcel,mRemote.transact(code, data, reply, 0)prefetch():transact(..., FLAG_ONEWAY)异步无返回
方法 ID(transaction code) 是编译期为每个接口方法分配的常量。
oneway →FLAG_ONEWAY,调用端不阻塞、服务端入队“异步队列”。
2.2 类型系统 & 方向限定
- 支持:基本类型、
String/CharSequence、Parcelable、List/Map<支持类型>、IBinder、FileDescriptor。 in/out/inout:决定数据是单向输入、单向输出还是双向(影响序列化与 copy 行为)。- Parcelable > Serializable:避免反射 & 临时对象,性能高。
2.3 版本演进与兼容
- 新增字段:Parcelable 末尾新增字段 → 旧端反序列化时默认忽略(要注意读写顺序一致)。
- 新增方法:最好新增接口版本号或feature flag(服务端可暴露
getVersion(),客户端按版本分支)。 - AIDL “稳定接口”/NDK AIDL(HAL 层)有更严格的 versioning 机制(VINTF/稳定 AIDL),App 层常用 Java AIDL 可用“软约定”做兼容。
3) 系统里哪里用了 Binder(有感知就能“举例 50 个”)
-
系统服务(全部 Binder) :
AMS/WMS/PMS/IMMS/Location/Audio/Power/Camera/Sensor/Clipboard/Notification… -
四大组件:
Activity/Service的调度都要和system_server讲(AMS)。ContentProvider的query/insert/update/delete都是远程过程调用。- 广播分发底层也走 Binder。
-
App 层典型:
Messenger、bindService + AIDL、MediaPlayer/MediaSession、ClipboardManager、JobScheduler、AlarmManager…
4) IPC 全家桶:怎么选
| 方案 | 底层 | 优点 | 适用 |
|---|---|---|---|
| AIDL/Binder | Binder | 高性能、强类型、同步/异步 | 复杂接口、系统/业务服务 |
| Messenger | Binder | 简单、Message 队列 | 轻量单请求/回执 |
| ContentProvider | Binder | 权限细粒度、URI 共享 | 跨进程数据访问 |
| Broadcast | Binder | 一对多通知 | 全局事件 |
| Socket/WebSocket | TCP/UDP | 跨设备/网络 | IM/长连接 |
| 共享内存 (ashmem/mmap) | 内核 | 超大数据、低 copy | 媒体/图像/模型 |
| 文件/DB | 文件系统 | 简单、持久 | 非实时共享 |
大对象 → 共享内存 + 传 fd;高频小 RPC → AIDL;一次多端通知 → Broadcast;纯数据共享 → Provider。
5) 工程实践:常见坑 & 最佳实践
- 避免大 Bundle:跨进程传大图/大数组 →
TransactionTooLargeException;改用共享内存/文件 + fd。 - 不要阻塞 Binder 线程:
onTransact()里甩到业务线程/线程池,快速返回。 - 连接健壮性:
linkToDeath+ 自动重连;拿不到服务先getService()再checkPermission()。 - 权限校验:服务端统一做
UID/PID检查、enforcePermission()/签名权限/SELinux domain。 - oneway 滥用:异步会在目标进程排队,过多会挤爆队列;对时序敏感的要同步。
- 接口演进:加
getVersion();新字段在尾部;旧端默认值;灰度双写。 - 线程池上限:默认 ~16;大量并发
onTransact()可能饿死,必要时拆分服务或限流。 - fd 生命周期:跨进程传 fd 后要明确由谁负责关闭,避免 fd 泄漏。
6) 面试“追问链”快答卡
- Q:Binder 为什么一次拷贝?
A:驱动在接收方 mmap 区域分配 buffer,把数据从发送方 user 拷到这块映射内存,接收方直接可见 → 只需一次 copy。 - Q:Binder 内核里对象怎么表示?
A:服务端binder_node,客户端binder_ref;一次调用是binder_transaction,数据放在binder_buffer。 - Q:AIDL 生成了什么?
A:IInterface接口 +Stub(服务端,继承Binder,onTransact分发)+Proxy(客户端,transact打包Parcel)。 - Q:为什么/何时用 oneway?
A:通知类/不关心返回值场景;注意过多 oneway 会把对端队列挤爆,且无背压回馈。 - Q:
TransactionTooLargeException为何发生?
A:每进程 Binder 事务缓冲约 1MB,含并发占用;一次传大对象/大 Bundle 就会爆;建议改用共享内存 + fd。 - Q:服务端如何鉴权?
A:Binder.getCallingUid()/getCallingPid()+ 权限(enforceCallingPermission)+ 签名校验 + SELinux。 - Q:ServiceManager 做什么?
A:系统服务注册/查询中心;addService/getService;system_server 启动时把 AMS/WMS/PMS 注册进去。
7) 小图记忆(ASCII)
css
复制编辑
Client(Proxy) --Parcel--> [Binder Driver] --Parcel--> Server(Stub)
\ mmap/一次拷贝 /
--------- UID/PID 安全校验 ---------/
8) 一句话套路收尾(面试总结)
Binder 是地基:一次拷贝、高安全、线程池、引用模型,系统服务全靠它;
AIDL 是工具:自动生成 Stub/Proxy,强类型 RPC;
IPC 选型看场景:小而频 → AIDL;一对多 → Broadcast;数据共享 → Provider;大数据 → 共享内存+fd;跨设备 → Socket。
工程关键:别阻塞 Binder 线程,别传大对象,权限要严,接口要可演进,故障要可恢复。