Binder 是 Android 中最核心的跨进程通信机制。它通过内核 Binder 驱动作为中转,让不同进程之间可以像调用本地方法一样调用远程对象的方法。
一、为什么 Android 需要 Binder IPC?
Android 是基于 Linux 的系统。Linux 本身已经有很多 IPC 方式,比如:
1. 管道 Pipe
2. 消息队列 Message Queue
3. 共享内存 Shared Memory
4. Socket
5. 信号 Signal
6. Semaphore
那 Android 为什么还要设计 Binder?
主要原因:
1. 性能更好
2. 安全性更好
3. 支持对象引用语义
4. 适合 C/S 架构
5. 能管理调用方身份 UID/PID
6. 支持死亡通知 DeathRecipient
7. 与 Android 系统服务体系天然结合
Android 系统大量核心服务都依赖 Binder,例如:
ActivityManagerService
WindowManagerService
PackageManagerService
PowerManagerService
InputMethodManagerService
ClipboardService
MediaService
SurfaceFlinger 部分交互
App 调用这些系统服务时,底层基本都是 Binder IPC。
例如:
startActivity()
getSystemService()
bindService()
ContentProvider.query()
ClipboardManager.getPrimaryClip()
这些操作最终都可能走 Binder。
二、什么是 IPC?
IPC,全称:
Inter-Process Communication
也就是:
进程间通信
Android 中每个 App 默认运行在独立进程中。不同进程的内存空间是隔离的。
比如:
App A 进程
App B 进程
system_server 进程
media_server 进程
surfaceflinger 进程
它们不能直接访问彼此的对象。
例如 App 进程里不能直接调用 system_server 进程中 AMS 对象的方法:
ams.startActivity(...)
因为 AMS 对象真实存在于 system_server 进程的内存空间中。
所以需要一种机制,让 App 进程可以“远程调用”AMS。
Binder 就是做这个事情的。
三、Binder 的一句话理解
Binder 可以从不同角度理解:
1. 从机制上看
Binder 是 Android 的一种 IPC 机制。
2. 从驱动上看
Binder 是 Linux 内核中的一个字符设备驱动,通常是
/dev/binder。
新版本 Android 中还有:
/dev/binder
/dev/hwbinder
/dev/vndbinder
分别服务于不同的 Binder 域。
3. 从对象上看
Binder 是一种可以跨进程传递的远程对象引用。
4. 从架构上看
Binder 是一种 Client-Server 通信架构。
例如:
Client:App 进程
Server:system_server 中的 AMS
Binder Driver:内核中转
ServiceManager:服务注册中心
四、Binder IPC 的核心角色
Binder 通信中一般有四个核心角色:
1. Client 客户端进程
2. Server 服务端进程
3. ServiceManager 服务管理器
4. Binder Driver 内核驱动
可以用这张图理解:
Client 进程
|
| 1. 查询服务
v
ServiceManager
|
| 2. 返回服务代理 Binder
v
Client 持有 Proxy
|
| 3. transact()
v
Binder Driver
|
| 4. 转发请求
v
Server 进程 Binder 实体
|
| 5. onTransact()
v
执行真正方法
1. Client
调用方。
例如普通 App 调用:
ActivityManager.getService().startActivity(...)
这个 App 就是 Client。
2. Server
服务提供方。
例如:
ActivityManagerService
WindowManagerService
PackageManagerService
这些系统服务运行在 system_server 中,它们就是 Server。
3. ServiceManager
ServiceManager 是 Binder 服务的注册中心。
它负责:
1. 注册服务 addService
2. 查询服务 getService
3. 返回服务的 Binder 引用
例如系统服务启动时会注册:
ServiceManager.addService("activity", activityManagerService);
ServiceManager.addService("window", windowManagerService);
ServiceManager.addService("package", packageManagerService);
App 使用时会查询:
IBinder binder = ServiceManager.getService("activity");
然后转换成对应接口:
IActivityManager am = IActivityManager.Stub.asInterface(binder);
4. Binder Driver
Binder Driver 是内核层组件,是 Binder IPC 的核心。
它负责:
1. 跨进程数据传递
2. Binder 对象引用管理
3. 线程唤醒
4. 调用转发
5. 权限身份传递 UID/PID
6. 死亡通知
7. 引用计数
用户空间进程不能直接访问另一个进程内存,所以需要 Binder Driver 做中转。
五、Binder 的整体调用流程
以 App 调用 AMS 为例。
假设 App 要启动 Activity:
startActivity(intent);
底层大致流程:
App 进程
↓
Activity.startActivity()
↓
Instrumentation.execStartActivity()
↓
IActivityTaskManager.startActivity()
↓
Binder Proxy.transact()
↓
Binder Driver
↓
system_server Binder 线程
↓
ActivityTaskManagerService.onTransact()
↓
真正执行 startActivity 逻辑
简化成:
Client 调用 Proxy 方法
↓
Proxy 把参数写入 Parcel
↓
调用 transact()
↓
Binder Driver 把数据送到 Server
↓
Server 的 Binder 线程收到请求
↓
Stub.onTransact()
↓
从 Parcel 读取参数
↓
调用真正的服务方法
↓
把结果写回 reply Parcel
↓
Binder Driver 返回给 Client
↓
Proxy 读取结果
六、Binder 通信中的 Proxy 和 Stub
这是面试重点。
如果你使用 AIDL,系统会生成一个接口文件对应的 Java 类,其中最重要的是:
Stub
Proxy
例如 AIDL:
interface IUserManager {
String getUserName(int uid);
}
生成后大致结构:
public interface IUserManager extends IInterface {
String getUserName(int uid);
abstract class Stub extends Binder implements IUserManager {
public static IUserManager asInterface(IBinder obj) {
if (obj == null) return null;
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof IUserManager) {
return (IUserManager) iin;
}
return new Proxy(obj);
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
switch (code) {
case TRANSACTION_getUserName:
data.enforceInterface(DESCRIPTOR);
int uid = data.readInt();
String result = this.getUserName(uid);
reply.writeNoException();
reply.writeString(result);
return true;
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IUserManager {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
public String getUserName(int uid) {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
String result;
data.writeInterfaceToken(DESCRIPTOR);
data.writeInt(uid);
mRemote.transact(TRANSACTION_getUserName, data, reply, 0);
reply.readException();
result = reply.readString();
data.recycle();
reply.recycle();
return result;
}
}
}
}
Stub 是什么?
Stub 在服务端。
它的作用是:
1. 继承 Binder
2. 接收客户端请求
3. 解析 Parcel 数据
4. 调用真正的服务端方法
5. 把结果写回 Parcel
服务端通常这样写:
private final IUserManager.Stub mBinder = new IUserManager.Stub() {
@Override
public String getUserName(int uid) {
return "user_" + uid;
}
};
Proxy 是什么?
Proxy 在客户端。
它的作用是:
1. 伪装成本地接口对象
2. 把方法调用转换成 transact
3. 把参数写入 Parcel
4. 等待远端返回结果
5. 从 reply 中读取返回值
客户端调用:
userManager.getUserName(100);
表面看像调用普通 Java 方法,实际是:
Proxy.getUserName()
↓
Parcel 写参数
↓
mRemote.transact()
↓
Binder Driver
↓
Server.onTransact()
七、Binder 为什么像“本地调用”?
因为 AIDL 生成的 Proxy 实现了同一个接口。
比如:
IUserManager userManager = IUserManager.Stub.asInterface(binder);
userManager.getUserName(100);
调用者并不关心这个对象是真本地对象,还是远程代理对象。
asInterface() 会判断:
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
如果服务端和客户端在同一个进程:
直接返回本地 Stub 对象
如果不在同一个进程:
返回 Proxy 代理对象
所以 Binder 同时支持:
本地调用优化
跨进程调用
八、Binder 底层一次数据拷贝原理
这是面试高频点。
传统 IPC,比如 Socket,通常需要两次拷贝:
发送进程用户空间
↓ copy_from_user
内核空间缓冲区
↓ copy_to_user
接收进程用户空间
也就是:
用户空间 → 内核空间 → 用户空间
两次拷贝。
Binder 通过 mmap 做优化。
Binder 的数据传输过程
每个使用 Binder 的进程都会打开 Binder 驱动:
open("/dev/binder")
然后通过:
mmap()
把一块内核空间映射到进程的用户空间。
接收方进程有一块 Binder 映射区:
Server 用户空间虚拟地址
↑ mmap 映射
内核 Binder buffer
当 Client 发送数据时:
Client 用户空间
↓ copy_from_user,一次拷贝
内核 Binder buffer
↓ Server 可通过 mmap 映射访问
Server 用户空间
所以 Binder 通常说是:
一次数据拷贝。
准确理解:
发送方数据从用户空间拷贝到内核 Binder buffer;
接收方通过 mmap 映射访问这块 buffer,不需要再从内核拷贝到接收方用户空间。
九、Binder 和共享内存的关系
Binder 本身不适合传输大数据。
Binder 单次事务大小有限制,常见限制大约是:
1MB 左右
严格来说是一个进程的 Binder transaction buffer 约 1MB,不是单个参数一定能完整占用 1MB。
如果传输大图片、大文件、大数组,可能出现:
TransactionTooLargeException
所以大数据通常用:
1. 文件描述符 FileDescriptor
2. Ashmem / SharedMemory
3. mmap
4. ContentProvider + Uri
5. Messenger/Socket/管道等组合
典型方案:
Binder 传控制命令和文件描述符
共享内存传大数据
十、Binder 线程模型
Binder 服务端不是主线程直接处理所有请求,而是有 Binder 线程池。
每个使用 Binder 的进程通常会和 Binder 驱动配合维护线程池。
服务端收到 Binder 请求后,Binder 驱动会唤醒服务端某个 Binder 线程处理请求。
例如 system_server 中 AMS、WMS 等服务都可能在 Binder 线程中被调用。
重要注意点
如果你写 AIDL 服务:
@Override
public void doSomething() {
// 这个方法不一定运行在主线程
}
它通常运行在:
服务端 Binder 线程池
所以:
1. 不要直接更新 UI
2. 注意线程安全
3. 如果要操作 UI,要切到主线程
4. 耗时任务也不要长期占用 Binder 线程
例如:
new Handler(Looper.getMainLooper()).post(() -> {
// 更新 UI
});
客户端调用会不会阻塞?
如果是普通同步 Binder 调用:
remote.getUserName()
客户端线程会阻塞等待服务端返回。
所以如果你在主线程调用远程服务,而远程服务执行很慢,可能导致客户端 ANR。
十一、同步 Binder 和异步 Binder
Binder 调用可以分为:
1. 同步调用
2. 异步调用 oneway
1. 同步调用
普通 AIDL 方法默认是同步的。
例如:
interface IUserManager {
String getUserName(int uid);
}
客户端调用:
String name = userManager.getUserName(100);
会阻塞直到服务端返回。
2. 异步调用 oneway
AIDL 中可以使用:
oneway interface IUserCallback {
void onChanged(String data);
}
或者:
interface IUserManager {
oneway void refresh();
}
oneway 表示:
客户端发出去就返回,不等待服务端执行完成。
特点:
1. 不能有返回值
2. 不能抛 checked exception
3. 调用是异步的
4. 服务端按顺序处理同一个 Binder 对象上的 oneway 请求
5. 仍然会消耗 Binder 事务队列
注意:
oneway 不是无限制消息队列。如果发送太快,仍然可能阻塞或导致队列堆积。
十二、Binder 的安全机制
Binder 安全性比传统 IPC 好的一个重要原因是:
Binder 驱动知道调用方的 UID 和 PID。
服务端可以通过:
Binder.getCallingUid()
Binder.getCallingPid()
获取调用方身份。
例如系统服务中经常做权限校验:
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
mContext.enforceCallingPermission(
android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
"Permission denied"
);
clearCallingIdentity 是什么?
系统服务中经常看到:
final long token = Binder.clearCallingIdentity();
try {
// 以 system_server 自己身份执行
} finally {
Binder.restoreCallingIdentity(token);
}
意思是:
清除当前 Binder 调用方身份,临时恢复成本进程自己的系统身份。
为什么需要?
比如 App 调用 system_server,system_server 接着访问某些资源。如果不清除身份,后续权限判断可能仍以调用方 App 的 UID 计算。
所以系统服务在某些内部操作前需要:
clearCallingIdentity()
完成后再:
restoreCallingIdentity()
这是 Framework 源码常见面试点。
十三、Binder 死亡通知 DeathRecipient
跨进程通信时,远程进程可能死亡。
Binder 提供死亡监听机制:
IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// 远端 Binder 所在进程死亡
}
};
binder.linkToDeath(deathRecipient, 0);
取消监听:
binder.unlinkToDeath(deathRecipient, 0);
常见用途:
1. 监听远程服务死亡
2. 自动重连服务
3. 清理客户端资源
4. 系统服务监听 App 进程死亡
例如 AMS 会通过 Binder 机制知道某个 App 进程死亡,然后清理该进程对应的 Activity、Service、Receiver 等记录。
十四、Binder 引用和 Binder 实体
Binder 跨进程不是直接把 Java 对象拷贝过去。
它传递的是:
Binder 对象引用
底层有两个概念:
Binder 实体 node
Binder 引用 ref
服务端真正拥有 Binder 实体。
客户端拿到的是 Binder 引用。
例如:
system_server 中 AMS 是 Binder 实体
App 进程拿到的是 AMS 的 Binder 代理引用
当客户端调用代理时:
Binder Driver 根据引用找到对应实体
再把请求转发到实体所在进程
十五、ServiceManager 是如何工作的?
ServiceManager 本身也是一个 Binder 服务,不过它比较特殊。
它相当于整个系统的“服务注册表”。
系统启动时:
system_server 启动 AMS/WMS/PMS 等服务
↓
ServiceManager.addService("activity", AMS)
ServiceManager.addService("window", WMS)
ServiceManager.addService("package", PMS)
App 获取系统服务:
IBinder b = ServiceManager.getService("activity");
IActivityManager am = IActivityManager.Stub.asInterface(b);
简化流程:
Client 向 ServiceManager 查询 "activity"
↓
ServiceManager 返回 AMS 的 Binder 引用
↓
Client 持有 AMS Proxy
↓
后续直接和 AMS 通信,不需要每次经过 ServiceManager
注意:
ServiceManager 只负责查找和注册,不负责转发每一次业务调用。
十六、Binder 和 AIDL 的关系
很多人会把 Binder 和 AIDL 混为一谈。
它们不是一回事。
Binder:底层 IPC 机制
AIDL:用于生成 Binder 通信代码的接口描述语言
也就是说:
AIDL 是 Binder 的上层封装和代码生成工具。
没有 AIDL 也能手写 Binder:
class MyBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
...
}
}
但手写很麻烦,所以 Android 提供 AIDL 帮我们生成:
Stub
Proxy
onTransact
transact
Parcel 读写代码
十七、一个 AIDL 使用示例
1. 定义 AIDL
IUserService.aidl
package com.example.ipc;
interface IUserService {
String getUserName(int uid);
void setUserName(int uid, String name);
}
2. 服务端 Service
public class UserService extends Service {
private final Map<Integer, String> users = new ConcurrentHashMap<>();
private final IUserService.Stub binder = new IUserService.Stub() {
@Override
public String getUserName(int uid) {
return users.get(uid);
}
@Override
public void setUserName(int uid, String name) {
users.put(uid, name);
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
3. Manifest 声明进程
<service
android:name=".UserService"
android:process=":remote"
android:exported="false" />
4. 客户端绑定服务
private IUserService userService;
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
userService = IUserService.Stub.asInterface(service);
try {
userService.setUserName(100, "张三");
String nameResult = userService.getUserName(100);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
userService = null;
}
};
绑定:
Intent intent = new Intent(this, UserService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
十八、Parcel 是什么?
Binder 传输数据用的是:
Parcel
Parcel 是一种高效的序列化容器,用于把数据打包成适合 Binder 传输的格式。
支持传输:
1. 基本类型 int、long、float、double
2. String
3. Parcelable
4. List、Map 中的可支持类型
5. Binder 对象
6. FileDescriptor
Parcelable 和 Serializable 的区别
面试很常问。
Serializable:Java 标准序列化,使用反射,性能相对较低,适合持久化或简单场景。
Parcelable:Android 专门设计,手动写入和读取字段,性能更高,适合 Binder 和 Intent 传输。
Binder/AIDL 中更推荐:
Parcelable
十九、Binder 的数据大小限制
Binder 事务缓冲区有限,常见大概:
1MB
如果通过 Intent 或 AIDL 传大对象,可能报:
android.os.TransactionTooLargeException
常见场景:
1. Intent 传大 Bitmap
2. Bundle 传大量数据
3. AIDL 返回大 List
4. Fragment/Activity 状态保存过大
解决方案:
1. 传 Uri,不传实际大数据
2. 数据放文件/数据库/缓存,通过 key 查询
3. 使用共享内存
4. 分页传输
5. 使用 FileDescriptor
二十、Binder 通信过程中的线程切换
以 App 调 system_server 为例:
App 主线程
↓ 发起 Binder 调用,当前线程阻塞
Binder Driver
↓ 唤醒 system_server Binder 线程
system_server Binder 线程执行服务方法
↓ 返回结果
Binder Driver
↓ 唤醒 App 主线程
App 主线程继续执行
如果 App 主线程调用一个耗时 Binder 方法:
remoteService.doHeavyWork();
那么主线程会等待服务端返回,可能 ANR。
所以建议:
1. 耗时 IPC 放子线程
2. 服务端方法不要阻塞 Binder 线程太久
3. 大量异步回调用 oneway 或 callback 机制
4. 注意死锁
二十一、Binder 死锁问题
Binder 调用是同步阻塞的,如果双向调用处理不好,可能死锁。
例如:
Client 主线程调用 Server,同步等待结果
Server 处理过程中又回调 Client 主线程
Client 主线程正在等 Server 返回,无法处理回调
结果双方等待
这可能造成死锁或 ANR。
解决方式:
1. 不要在主线程做耗时同步 IPC
2. 回调尽量用 oneway
3. 服务端不要持锁调用远程 Binder
4. 锁粒度要小
5. 跨进程回调切异步线程处理
二十二、Binder 和 Messenger 的关系
Messenger 是基于 Binder 的上层封装。
它内部通过:
IMessenger.aidl
Handler
Message
实现跨进程消息通信。
特点:
1. 使用简单
2. 串行处理消息
3. 不适合高并发
4. 本质还是 Binder
适合简单 IPC。
AIDL 适合复杂接口、多方法、高并发调用。
二十三、Binder 和 ContentProvider 的关系
ContentProvider 也是基于 Binder 实现跨进程访问。
例如:
getContentResolver().query(uri, ...)
底层流程:
ContentResolver
↓
AMS 获取 Provider Binder
↓
IContentProvider.query()
↓
Binder IPC 到目标进程
↓
目标 ContentProvider 执行 query()
所以 ContentProvider 也属于 Binder IPC 的封装之一。
二十四、Binder 和系统服务调用
当你写:
ClipboardManager cm =
(ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
本质上会拿到系统服务代理。
调用:
cm.setPrimaryClip(clipData);
底层可能是:
ClipboardManager
↓
IClipboard Proxy
↓
Binder transact
↓
system_server ClipboardService
类似地:
ActivityManager → AMS
WindowManager → WMS
PackageManager → PMS
PowerManager → PowerManagerService
二十五、Binder 的 Java 层、Native 层、Kernel 层
Binder 是分层实现的。
Java 层:
Binder、IBinder、IInterface、Parcel、AIDL Stub/Proxy
Native 层:
BBinder、BpBinder、IPCThreadState、ProcessState、Parcel
Kernel 层:
Binder Driver,/dev/binder
Java 层常见类
android.os.IBinder
android.os.Binder
android.os.IInterface
android.os.Parcel
android.os.ServiceManager
Native 层常见类
BpBinder:客户端代理
BBinder:服务端实体
IPCThreadState:负责和 Binder 驱动交互
ProcessState:进程级 Binder 状态管理
Kernel 层常见结构
源码版本不同会变化,常见概念:
binder_proc:表示一个使用 Binder 的进程
binder_thread:表示一个 Binder 线程
binder_node:表示 Binder 实体
binder_ref:表示 Binder 引用
binder_transaction:表示一次 Binder 事务
binder_buffer:传输数据缓冲区
二十六、Binder 调用底层简化流程
一次同步 Binder 调用大致是:
1. Client Proxy 调用 transact()
2. Parcel 打包参数
3. Native 层通过 ioctl 写入 Binder 驱动
4. Binder 驱动找到目标 binder_node
5. Binder 驱动选择 Server 进程中的 Binder 线程
6. Server Binder 线程被唤醒
7. Server Stub.onTransact() 解析请求
8. 执行真正业务方法
9. 将返回值写入 reply Parcel
10. ioctl 返回给 Binder 驱动
11. Binder 驱动唤醒 Client 等待线程
12. Client Proxy 读取 reply
核心系统调用主要是:
ioctl()
mmap()
open()
二十七、Binder 为什么性能好?
主要原因:
1. 只需要一次数据拷贝
2. 使用 mmap 优化接收缓冲区
3. 支持对象引用传递,避免重复建立连接
4. 驱动层直接管理事务和线程唤醒
5. 比 Socket 更适合本机 C/S IPC
不过 Binder 不是万能的。
不适合:
1. 大数据传输
2. 高频大量小包无节制发送
3. 长时间阻塞调用
4. 跨设备网络通信
二十八、Binder 为什么安全?
Binder 安全性主要来自:
1. UID/PID 由内核 Binder 驱动提供,不能伪造
2. Server 可以检查调用方权限
3. Binder 引用由驱动维护,不是随便构造的地址
4. ServiceManager 控制服务注册和查询权限
5. 支持 SELinux 策略约束
系统服务常用:
Binder.getCallingUid()
Binder.getCallingPid()
checkCallingPermission()
enforceCallingPermission()
二十九、Binder 常见面试题
下面是高频问题和建议回答。
1. Binder 是什么?
回答:
Binder 是 Android 的核心 IPC 机制,用于不同进程之间通信。它基于内核 Binder 驱动实现,采用 Client-Server 架构,并通过 ServiceManager 管理系统服务。应用通过 Binder 可以像调用本地方法一样调用远程服务。
2. Binder 通信有哪些角色?
回答:
Client:调用方
Server:服务提供方
ServiceManager:服务注册和查询中心
Binder Driver:内核层中转和管理者
3. Binder 通信流程是怎样的?
回答:
Client 通过 Proxy 调用方法
→ 参数写入 Parcel
→ transact 进入 Binder 驱动
→ Binder 驱动转发到 Server 进程
→ Server Stub.onTransact 解析参数
→ 调用真正服务方法
→ 结果写回 Parcel
→ Binder 驱动返回给 Client
→ Client Proxy 读取结果
4. Stub 和 Proxy 的区别?
回答:
Stub:服务端 Binder 实体,负责接收请求、解析 Parcel、调用真正方法。
Proxy:客户端代理对象,负责把本地方法调用转换成 Binder transact。
5. AIDL 和 Binder 的关系?
回答:
Binder 是底层 IPC 机制,AIDL 是接口描述语言,用来自动生成基于 Binder 的 Stub、Proxy、Parcel 读写代码。AIDL 是对 Binder 的封装,不是 Binder 本身。
6. Binder 为什么只需要一次拷贝?
回答:
Binder 使用 mmap 将内核 Binder buffer 映射到接收方进程。发送方数据通过一次 copy_from_user 拷贝到内核 Binder buffer,接收方可以通过映射访问这块 buffer,因此相比传统 IPC 少了一次从内核到接收方用户空间的拷贝。
7. Binder 的大小限制是多少?
回答:
Binder transaction buffer 通常约 1MB,是进程级共享缓冲限制。传输大数据可能抛出 TransactionTooLargeException。大数据应使用文件、Uri、共享内存或 FileDescriptor。
8. Binder 方法运行在哪个线程?
回答:
服务端 Binder 方法通常运行在服务端进程的 Binder 线程池中,不一定是主线程。所以要注意线程安全,不能直接更新 UI,也不要长时间阻塞 Binder 线程。
9. 客户端调用 Binder 会阻塞吗?
回答:
普通 Binder 调用是同步阻塞的,客户端线程会等待服务端执行完成并返回。如果在主线程调用耗时远程方法,可能导致 ANR。AIDL 中可以使用 oneway 实现异步调用。
10. oneway 有什么特点?
回答:
1. 异步调用
2. 客户端不等待返回
3. 方法不能有返回值
4. 不能抛 checked exception
5. 请求会进入 Binder 队列,仍可能因队列满而阻塞
11. Binder 如何做权限校验?
回答:
Binder 驱动会携带调用方 UID/PID,服务端可以通过 Binder.getCallingUid() 和 Binder.getCallingPid() 获取调用者身份,并使用 checkCallingPermission 或 enforceCallingPermission 做权限校验。
12. linkToDeath 是什么?
回答:
linkToDeath 用于监听远程 Binder 所在进程死亡。当远程进程死亡时,会回调 DeathRecipient.binderDied(),常用于服务重连和资源清理。
13. Binder 和 Socket 相比有什么优势?
回答:
1. Binder 专为本机 IPC 设计,性能更好
2. 一次拷贝,Socket 通常两次拷贝
3. 支持 UID/PID 身份传递
4. 支持对象引用和死亡通知
5. 与 Android 系统服务架构深度结合
但 Socket 适合网络通信,Binder 主要用于本机 IPC。
14. Binder 为什么不适合传大数据?
回答:
Binder transaction buffer 有大小限制,频繁传大数据会占用缓冲区并可能触发 TransactionTooLargeException。Binder 更适合传控制命令、小对象和文件描述符,大数据应通过共享内存、文件或 Uri 传递。
15. 什么是 Binder 驱动?
回答:
Binder 驱动是内核层的字符设备驱动,通常对应 /dev/binder。它负责 Binder 事务转发、进程和线程管理、Binder 引用管理、数据缓冲区管理、调用方身份传递和死亡通知。
三十、面试回答模板
如果面试官问:
你讲一下 Binder IPC。
你可以这样回答:
Binder 是 Android 的核心 IPC 机制,主要用于不同进程之间通信。它采用 Client-Server 架构,核心角色包括 Client、Server、ServiceManager 和 Binder Driver。
以 AIDL 为例,客户端拿到的是远程服务的 Proxy,调用接口方法时,Proxy 会把参数写入 Parcel,然后调用 transact 进入 Binder 驱动。Binder 驱动根据 Binder 引用找到服务端 Binder 实体,并唤醒服务端 Binder 线程。服务端 Stub 的 onTransact 会解析 Parcel,调用真正的业务方法,再把结果写回 reply Parcel,最后通过 Binder 驱动返回给客户端。
Binder 的优势是性能和安全性。性能上,它通过 mmap 让接收方映射内核 Binder buffer,数据通常只需要从发送方用户空间拷贝到内核一次。安全上,Binder 驱动会携带调用方 UID/PID,服务端可以做权限校验。另外 Binder 支持对象引用、死亡通知和线程池机制。
需要注意的是,普通 Binder 调用是同步阻塞的,服务端方法通常运行在 Binder 线程池中,因此不能长时间阻塞,也不能直接操作 UI。Binder transaction buffer 大小有限,大数据传输应该使用共享内存、文件描述符或 Uri。
这段回答比较完整,面试中够用了。
三十一、最核心总结
最后帮你压缩成几个必须记住的点:
1. Binder 是 Android 最核心的 IPC 机制。
2. Binder 基于内核 Binder 驱动实现。
3. 核心角色:Client、Server、ServiceManager、Binder Driver。
4. AIDL 会生成 Stub 和 Proxy。
5. Proxy 在客户端,Stub 在服务端。
6. 参数和返回值通过 Parcel 序列化。
7. 普通 Binder 调用是同步阻塞的。
8. 服务端方法运行在 Binder 线程池。
9. Binder 使用 mmap,通常只需一次数据拷贝。
10. Binder transaction buffer 有大小限制,约 1MB。
11. Binder 驱动会传递调用方 UID/PID,便于权限校验。
12. linkToDeath 可以监听远程 Binder 死亡。
13. Android 系统服务基本都基于 Binder 通信。
一句话最终总结:
Binder 是 Android 中连接 App 进程、system_server 和各种系统服务的底层通信桥梁,是 Android Framework 能够正常运转的核心基础。