Android中的Binder机制是Android系统中最核心和最基础的进程间通讯机制。
1 什么是进程间通讯机制(IPC)?
众所周知,Android系统基于Linux开发,Linux系统里面本来就有进程间通讯机制。
1.1 Linux的IPC(Inter-Process Communication)概览
它是不同进程之间交换数据和消息的一种手段。
但是进程之间默认地址空间是隔离的,有什么解决方案去做到不同进程之间交换信息吗?答案是当然有。
下图整理自AI。
类型 | 示例 | 特点说明 |
---|---|---|
文件系统相关 | 管道、命名管道(FIFO) | 简单,适合父子进程或有命名路径的通信 |
内存相关 | 共享内存 | 高效,速度快,但需自行同步(如用信号量) |
消息传递 | 消息队列、信号、信号量 | 安全、结构化,但效率略低 |
套接字通信 | 本地 Socket、网络 Socket | 灵活,可跨主机,适用于客户端-服务器架构 |
高级通信机制 | DBus、ZeroMQ、gRPC、Binder等 | 抽象层高,适合复杂或跨语言系统 |
对于这个列表,我们重点是需要关注这些方案的原理是什么。只有知道原理,我们才能解开现象背后的本质,对于很多技术难题,我们才能有思路举一反三。
1.2 文件系统相关(管道、命名管道 FIFO)
这类IPC本质上是一个内核缓冲区,读写操作通过文件描述符来跟缓冲区进行交互。
写端进程通过write写入缓冲区,读端进程通过read读取数据。
这种属于阻塞式通讯,读和写都需要等对方准备好,系统通过文件描述符来判断是否可以进行读和写。
1.3 内存相关(共享内存)
这类IPC,在不同进程的虚拟地址空间通过映射的方式,比如mmap来映射到一段共同的物理内存页来实现“共享”。
进程通过指针直接访问内存中的数据结果,完全绕过了内核的读写接口,所以速度极快。俗称“零拷贝”。这种机制是没有内建锁的,进程需要通过锁机制等保证并发安全。
1.4 消息传递类(消息队列、信号、信号量)
消息队列:内核维护一个消息队列结构体,发送进程通过系统调用把消息copy到内核队列中(第一次拷贝)。接收方从队列中获取信息(第二次拷贝)。
信号:信号是系统内核或者进程向另一个进程发送的异步通知,用来通知某事件的发生。这是一种轻量级的IPC方式。
核心原理是内核使用位图或者信号队列标记进程收到的信号,调度器在合适的时候调用信号处理函数。
信号量:是一种同步原语。本质上是内核中的一个整数值结构 + 等待队列,用于资源访问同步。
P/V 操作中,P(等待)将信号量值减一,若 < 0 进程阻塞;V(释放)加一,并唤醒等待队列中的进程。
1.5 套接字通信(本地 / 网络 Socket)
Linux 为每个 Socket 创建一个 socket 缓冲区结构(sk_buff),内核中维护双向队列。
本地 Socket 使用 UNIX Domain 协议族,数据在内核中传输,无需网络协议栈处理。
网络 Socket 走 TCP/IP 协议栈,数据被封包、路由、拥塞控制,完整链路层处理。
2 Android特有的IPC:Binder
2.1 Binder机制的有什么用?
Binder 是 Android 提供的一种高性能的 IPC 机制,用于实现:
- 跨进程方法调用(如 A 应用调用系统服务中的方法)
- 客户端-服务端架构模型
- 安全的权限校验
- 引用计数和生命周期管理
2.2 Binder机制为什么快?
可以先看下传统的消息传递类IPC做法。
图片来源:https://blog.csdn.net/carson_ho/article/details/73560642
而binder通信机制基于内核 Binder 驱动,使用内核内存映射区(Binder Buffer)做 zero-copy 通信。来看下Binder的做法。
图片来源:https://blog.csdn.net/carson_ho/article/details/73560642
他的通讯流程是:发送进程 ->内核缓冲区(第一次拷贝) ->映射到接收进程。
2.2 Binder机制由什么组成?
Binder 是一个 C/S 模型(客户端/服务端),主要组成部分如下:
-
ServiceManager
系统服务注册与发现的“电话簿”,所有服务通过它注册和查找。 -
Binder 驱动(Binder Driver)
位于内核空间(/dev/binder),实现核心的 IPC 逻辑,如数据传输、线程调度、引用计数等。 -
Binder 本地端(Client 端)
通过代理类(Proxy)与服务端通信,通常由 AIDL 自动生成。 -
Binder 服务端(Server 端)
实现具体的业务逻辑,继承 Binder 类并重写方法。 -
Binder线程池(Binder线程)
服务端进程中专门处理来自 Binder 驱动的调用请求的线程(由 BinderThreadPool 管理)。
下面是一个App调用系统AMS的一个例子:
3 常见问题
3.1 如果超过16个进程同时在使用AIDL进行通讯,会发生什么?
首先,16 并不是硬性限制,“16个进程”来源于 Android Binder 线程池默认上限,它是一个服务端并发处理能力的限制,而不是进程数量的限制。
Android 中,服务端 Binder 对象默认最多有 16 个 Binder 线程(可通过 android.os.Binder.setThreadPoolMaxThreadCount(int) 设置)。
而这个限制影响的是,服务端一次性能并发处理的 Binder 请求线程数。
并不是说客户端数量最多 16 个,而是服务端的同时“处理线程”是 16。
那么如果超过 16 个进程同时通过 AIDL 访问一个服务,会发生什么呢?
-
请求不会失败,但需要排队等待,如果服务端的 16 个 Binder 线程都在处理请求,第 17 个及之后的请求将进入等待队列。
-
客户端 transact() 系统调用会阻塞,直到服务端有空闲线程。
-
服务响应变慢(可观的延迟)。随着并发请求数增加,响应时间线性上升,尤其是服务端执行耗时任务时。比如,图像处理、数据库查询等在主线程处理会导致严重卡顿。
-
线程池饱和后,部分请求可能被拒绝(极端情况)。如果服务端主动设置了最大等待队列长度(如基于 ThreadPoolExecutor),也可能在高负载时拒绝新请求(抛出 RejectedExecutionException)。但这取决于服务的实现方式,Binder 驱动本身仍是队列化的。
-
主线程阻塞风险,如果服务端是 onTransact() 或处理逻辑运行在主线程上,那么并发调用容易造成 ANR(应用无响应)。Google 明确建议服务端操作应放到 HandlerThread / BinderThreadPool / JobScheduler 等后台线程。
后续继续补充。。。