推荐选择方式
场景 | 推荐通信方式 |
---|---|
两个组件属于同一 App | Handler、Messenger、AIDL(同包) |
两个 App 通信 | AIDL、ContentProvider、BroadcastReceiver |
进程通信大量数据 | Binder 或 mmap + Socket |
简单消息通知 | BroadcastReceiver、Messenger |
数据存储共享 | ContentProvider、共享文件 |
先说Binder ipc通信的底层原理 | |
再说Binder模型。 |
一次完整的 Binder IPC 通信过程通常是这样:
binder通信原理是一次数据拷贝+内存映射完成的
只有一块数据接收缓存区
- 服务端启动的时候调用mmap和binder建立内存映射通道,在服务端的用户空间开辟一块虚拟的地址空间,内核将服务端的这个虚拟空间地址记录,此时没有进行真正的内存映射
- 客户端发起数据传输的时候,内核才为服务端这个虚拟空间开辟物理地址空间,才真正进行了内存映射,
- 客户端通过系统 copyfromuser() 将数据发送给内核时,内核将客户端数据拷贝到这个地址空间,由于这个地址空间和服务端的地址空间存在内存映射,数据相当于直接发送到了服务端的地址空间。
服务端一开始就会轮训这个地址空间是否有数据,没有的话就会阻塞,有数据了,内核会唤醒服务端读数据。
服务端调用 mmap()
时:
- 并不会立刻分配 binder buffer;
- 只是在服务端进程地址空间里创建了一块虚拟内存区域(mmap 区域);
- 这块区域的实际物理内存是由内核 binder 驱动 在"真正需要时"才分配的 ------ 即客户端发起事务时。
换句话说:
服务端
mmap()
是**"预先建立通道" ,但真正的数据 buffer 是在事务发送时分配的**。

Binder 通信模型
- 首先,一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;
- Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。
- Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。
注意:
1,ServiceManager进程是使用BINDER_SET_CONTEXT_MGR将自己注册成ServiceManager,并会创建一个Binder 实体
2,这个 Binder 实体的引用在所有 Client 中Binder 引用编号(int)中都固定为 0 ,无需通过其它手段获得。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通信。

当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己。当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了。
性能
- 性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
- 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。

再说说稳定性,Binder 基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。
安全性, 服务端通过pid验证客户端,进行权限控制
内存映射
内存映射(mmap)本质上是将 「用户进程的虚拟地址空间」映射到 「一块内核控制的物理内存页」 ,从而实现内核与用户空间对同一块物理内存的共享访问。
📌 更准确地说:
内存映射 并不是直接映射"虚拟地址 ↔ 虚拟地址" ,而是:
✅ 将:
-
内核中管理的一块物理页
-
映射到:
- 服务端进程的用户空间虚拟地址(mmap区域)
- 同时这块物理页也是内核中 binder buffer 的一部分
这样,内核和服务端进程就能访问同一块实际物理内存,实现高效的零拷贝通信(read/write 不需要额外 copy)。