1.Android Binder 框架中 BpXXX 与 BnXXX 架构总结
一、核心类关系
-
接口层
├─ IXXX: 定义业务逻辑接口(如图中 IMediaPlayer)
│
├─ BpXXX: 客户端代理 (Proxy)
│ ├─ 继承关系: public IXXX, public BpRefBase
│ └─ 核心成员: mRemote (BpBinder*)
│
└─ BnXXX: 服务端实现 (Native)
├─ 继承关系: public IXXX, public BBinder
└─ 业务载体: MediaPlayerService::Client -
通信基类
├─ IBinder: 进程间通信核心接口
├─ BpBinder: 客户端通信实体
└─ BBinder: 服务端通信实体 -
驱动交互层
└─ BinderDriver: 通过 ioctl 系统调用交互
二、BpXXX (客户端代理) 核心机制
-
工作流程:
客户端调用 → 打包 binder_transaction_data → mRemote.transact() → Binder驱动 -
核心组件:
- BpRefBase::remote(): 获取 BpBinder 驱动接口
- IPCThreadState: 通过 transact() 发起通信
- Parcel 数据封装: 序列化调用参数
-
功能特点:
▸ 对客户端透明隐藏 IPC 细节
▸ 同步等待服务端返回结果
▸ 维护与远程服务的连接状态
三、BnXXX (服务端实现) 核心机制
-
工作流程:
Binder驱动接收请求 → BBinder::transact() → BnXXX::onTransact() → 执行业务逻辑 -
核心组件:
- onTransact() 方法: 解析请求并分派到具体业务
- 业务实现类: 如 MediaPlayerService::Client
- ObjectManager: 管理服务对象生命周期
-
功能特点:
▸ 实现具体业务逻辑(如音视频播放控制)
▸ 响应客户端请求并返回结果
▸ 运行在服务端进程的 Binder 线程池中
四、IPC 通信全流程
客户端进程 Binder驱动 服务端进程
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
│ BpXXX │ │ │ │ BnXXX │
│ - 方法调用 │1 │ │3 │ - onTransact() │
│ - Parcel打包 ├─────►│ 消息路由 ├─────►│ - 业务执行 │
│ │ │ │ │ │
│ 等待响应 ◄────────┤4 │ │2 │ 返回结果 ◄───────┤
└─────────────────┘ └──────────────┘ └─────────────────┘
步骤说明:
- BpRefBase::remote() 调用 BpBinder
- BinderDriver 通过 ioctl 传递消息
- IPCThreadState 唤醒服务端线程
- 反向路径返回执行结果
五、架构设计
-
接口统一性
- BpXXX 和 BnXXX 共享相同的 IXXX 接口
- 保证客户端调用与服务端实现的一致性
-
双向通信
- 客户端可调用服务端方法(BpXXX → BnXXX)
- 服务端可主动通知客户端(通过 callback 接口)
-
线程模型
- PoolThread: 维护 Binder 线程池
- ProcessState: 管理进程级 Binder 资源
- IPCThreadState: 管理线程级通信状态
2.transact()和
onTransact()
一. 函数定义与角色定位
-
transact()
- 定义:位于客户端代理对象(
Proxy
类)中,由AIDL自动生成。 - 作用:将客户端的调用请求打包为
Parcel
数据,通过Binder驱动发送到服务端。 - 调用路径:客户端调用接口方法 →
Proxy
类方法 →transact()
→ Binder驱动 → 服务端。
- 定义:位于客户端代理对象(
-
onTransact()
- 定义:位于服务端
Stub
类(继承Binder
)中,由AIDL自动实现。 - 作用:接收Binder驱动转发的
Parcel
数据,解析请求参数,调用服务端实际业务逻辑,并将结果返回。 - 调用路径:Binder驱动 →
Stub.onTransact()
→ 服务端业务方法 → 结果返回客户端。
- 定义:位于服务端
二. 执行流程与线程模型
-
客户端发起请求
- 客户端调用AIDL接口方法(如
getSum()
)。 Proxy
类创建Parcel
对象(_data
写入参数,_reply
预留返回结果),调用transact()
。- 客户端线程阻塞,等待服务端响应。
- 客户端调用AIDL接口方法(如
-
服务端处理请求
- Binder驱动将请求路由到服务端进程的Binder线程池。
- 线程池中某个线程执行
Stub.onTransact()
:- 解析
Parcel
中的方法标识符(code
参数,如TRANSACTION_addBook
)。 - 从
Parcel
中读取参数,调用服务端对应的业务方法(如addBook()
)。 - 将返回值写入
_reply
。
- 解析
-
结果返回客户端
- 服务端处理完成后,Binder驱动将
_reply
返回客户端。 - 客户端线程唤醒,从
_reply
中读取结果并返回。
- 服务端处理完成后,Binder驱动将
线程模型对比
函数 | 执行位置 | 线程特性 |
---|---|---|
transact() | 客户端进程 | 调用线程阻塞(如主线程需防ANR) |
onTransact() | 服务端Binder线程池 | 异步执行,需线程安全设计 |
三. 参数解析与数据封装
-
transact()
的关键参数code
:方法标识符(由AIDL自动生成),用于服务端区分调用的方法。data
:输入型Parcel
,封装方法参数。reply
:输出型Parcel
,预留结果存储空间。flags
:控制调用行为(如FLAG_ONEWAY
表示异步调用)。
-
onTransact()
的关键参数code
:与transact()
中的code
对应,标识目标方法。data
:接收客户端发送的Parcel
,需按顺序读取参数。reply
:向其中写入结果,返回给客户端。
Parcel的作用
- 作为跨进程数据传输的容器,支持基本类型、
Parcelable
对象、Binder
引用等。 - 序列化/反序列化由系统自动处理,开发者只需按顺序读写数据。
四. 设计原理与异常处理
-
协同关系
transact()
和onTransact()
通过相同的code
标识符和对称的Parcel
读写顺序实现方法映射与参数传递,构成一套类RPC(远程过程调用)的IPC机制。 -
异常场景
- 服务端进程崩溃:
transact()
抛出RemoteException
,客户端需通过linkToDeath()
监听Binder死亡通知以重连。 - 跨进程类型安全:自定义
Parcelable
对象需严格保持读写一致性,否则会导致Parcel
数据解析错位。
- 服务端进程崩溃:
-
性能优化
- 单向调用(
FLAG_ONEWAY
):添加oneway
关键字可使调用非阻塞,但无返回值。 - Binder线程池:默认最大16个线程,高并发时需优化服务端业务逻辑的耗时。
- 单向调用(
-
两者关系图示
客户端进程 服务端进程
│ │
│ 1. 调用AIDL接口方法 │
│ 2. Proxy.transact() │
│ - 打包Parcel (_data) │
│ - 发送请求 → Binder驱动 → │
│ │ 3. Binder线程池调用Stub.onTransact()
│ │ - 解析code标识方法
│ │ - 从_data读取参数
│ │ - 执行实际业务逻辑
│ │ - 结果写入_reply
│ 4. 收到响应 ← Binder驱动 ← │
│ - 从_reply解析结果 │
│ 5. 返回结果 │
核心关系:transact()
是跨进程调用的发起端,onTransact()
是执行端,两者通过Binder驱动和Parcel
数据容器协作,实现跨进程通信的透明化调用。