Binder和IBinder
核心概念:为什么需要 Binder?
在 Android 中,应用程序(进程)是相互隔离的。一个应用不能直接访问另一个应用的内存空间。但是,系统服务(如 ActivityManager
, WindowManager
)运行在独立的系统进程(如 system_server
)中,应用需要与它们通信;此外,应用之间也可能需要共享数据或功能。这就需要一种进程间通信(IPC) 机制
Binder 就是 Android 自己实现的、高效、安全的 IPC 机制。它相比于传统的 Linux IPC(如管道、信号量、共享内存、Socket等)具有以下优势:
- 高性能:只需要一次数据拷贝,性能仅次于共享内存
- 安全性:基于调用进程的 UID/PID 进行身份验证,支持精细的权限控制
- 面向对象:将 IPC 调用伪装成本地方法调用,对开发者更友好
- 引用计数:内置对跨进程对象生命周期的管理
一. android.os.IBinder
接口
IBinder
是 Binder 通信机制中的核心接口。它定义了跨进程通信的基本协议,是所有可以通过 Binder 进行 IPC 的对象的抽象
你可以把它理解为一个远程对象的令牌或引用。无论这个对象是在本进程还是远程进程,你都可以通过其 IBinder
引用来与它交互
主要作用:
- 核心 IPC 功能:
○transact(int code, Parcel data, Parcel reply, int flags)
:执行一个远程调用。这是最核心的方法
◎code
:用户定义的交易ID,用于标识调用哪个方法
◎data
:包含调用参数的Parcel
对象
◎reply
:用于存放返回结果的Parcel
对象(如果不需要回复,可以为 null)
◎flags
:例如0
表示普通 RPC,FLAG_ONEWAY
表示异步调用,不等待回复
○linkToDeath(IBinder.DeathRecipient recipient)
和unlinkToDeath
:注册一个“死亡通知”,当 Binder 所在进程终止时,你会收到回调。这对于清理资源非常重要 - 对象信息查询:
○String getInterfaceDescriptor()
:获取 Binder 对象支持的接口描述符,通常是其全限定类名
○boolean isBinderAlive()
:检查 Binder 对象所在的进程是否还存活 - 查询远程接口:
○IInterface queryLocalInterface(String descriptor)
:尝试将IBinder
对象转换回本地的IInterface
对象。如果该 Binder 对象就在本地进程,则转换成功;如果在远程,则返回null
总结:IBinder
是一个“不透明”的通信句柄。你通常不直接使用它,而是通过由 AIDL 生成的更高级的接口来工作。但所有 Binder 对象都实现了这个接口
二. android.os.Binder
类
Binder
类是 IBinder
接口的一个具体实现。它充当了 Binder IPC 机制中的服务器端本地对象和客户端远程代理的基类
当你在 AIDL 中定义一个接口时,编译工具会自动生成一个继承自 Binder
的类(例如 Stub
类)
主要角色和功能:
- 本地 Binder 对象(服务实现方):
○ 你的服务实现类(例如MyService.Stub
)会继承自Binder
○ 它重写了onTransact(int code, Parcel data, Parcel reply, int flags)
方法。这个方法负责解析客户端发来的请求(读取data
中的参数),调用本地对应的方法,然后将结果写入reply
○ 它是一个在服务端进程中的普通 Java 对象 - Binder 代理对象(BinderProxy):
○ 在客户端进程中,你拿到的实际上是一个BinderProxy
对象。这个类是Binder
的一个内部类,它也实现了IBinder
接口
○ 当你调用代理对象的方法时(例如aidlInterface.someMethod()
),代理内部会调用transact()
方法,将方法标识和参数打包成Parcel
,通过内核 Binder 驱动发送给服务端
○BinderProxy
的transact()
方法是 IPC 发生的客户端起点
关键方法:
boolean onTransact(int code, Parcel data, Parcel reply, int flags)
:服务端核心方法。子类必须重写此方法来处理来自客户端的请求。返回true
表示交易成功处理final boolean execTransact(...)
:由系统内部调用,是连接 JNI 层和onTransact
的桥梁。通常不需要你关心static final native long clearCallingIdentity()
和static final native void restoreCallingIdentity(long token)
:在服务端代码中,用于清除和恢复调用方的身份。当服务端需要以其自身权限执行某些操作,而不是以客户端权限时,会用到这两个方法static final native int getCallingPid()
和static final native int getCallingUid()
:在服务端获取调用方的 PID 和 UID,用于权限验证
三. Binder IPC 的工作流程(结合 AIDL)
让我们通过一个典型的 AIDL 例子来看 IBinder
和 Binder
是如何协作的
1)定义 AIDL 接口 (IMyService.aidl
)
interface IMyService {int add(int a, int b);
}
2)编译后生成的 Java 文件(简化版)
// 生成的接口,继承自 IInterface
public interface IMyService extends android.os.IInterface {// 客户端使用的代理类public static class Proxy implements IMyService {private android.os.IBinder mRemote; // 核心:持有一个 IBinder 引用Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}@Overridepublic int add(int a, int b) throws android.os.RemoteException {// 准备输入 Parcelandroid.os.Parcel _data = android.os.Parcel.obtain();// 准备输出 Parcelandroid.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(a);_data.writeInt(b);// !!!核心调用:通过 IBinder 发起 transact !!!mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();// 读取结果_result = _reply.readInt();} finally {_reply.recycle();_data.recycle();}return _result;}}// 服务端使用的 Stub 类,继承自 Binder,并实现了 IMyService 接口public static abstract class Stub extends android.os.Binder implements IMyService {@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {// !!!核心处理:在服务端处理 transact 请求!!!switch (code) {case TRANSACTION_add: {data.enforceInterface(DESCRIPTOR);int _arg0 = data.readInt(); // 读取参数 aint _arg1 = data.readInt(); // 读取参数 b// 调用本地实现的方法int _result = this.add(_arg0, _arg1);reply.writeNoException();// 将结果写入 replyreply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}// 将 IBinder 转换为 IMyService 接口的辅助方法public static IMyService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}// 先查询本地接口,看 obj 是否就在本进程android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof IMyService))) {// 如果是,直接返回本地对象return (IMyService) iin;}// 如果不是,则返回一个代理对象 Proxyreturn new IMyService.Proxy(obj);}}
}
3)流程总结
- 服务端:创建一个具体的服务对象,继承自
Stub
(而Stub
继承自Binder
),并实现add
方法private final IMyService.Stub mBinder = new IMyService.Stub() {@Overridepublic int add(int a, int b) {return a + b;} }; // 在 onBind 中返回这个 Binder @Override public IBinder onBind(Intent intent) {return mBinder; }
客户端:通过
ServiceConnection
连接到服务,在onServiceConnected
回调中收到一个IBinder
对象private IMyService myService;private ServiceConnection connection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 使用 Stub.asInterface 将 IBinder 转换为 IMyService 接口myService = IMyService.Stub.asInterface(service);} };
调用:客户端调用
myService.add(1, 2)
○myService
实际上是一个Proxy
对象
○Proxy.add
方法将参数打包到_data
,然后调用mRemote.transact()
○mRemote
就是内核 Binder 驱动在客户端创建的BinderProxy
对象IPC:
BinderProxy.transact()
通过 JNI 进入 Native 层,最终通过内核的 Binder 驱动,将请求发送到服务端进程服务端处理:服务端进程中的
Binder
对象收到请求,其execTransact
方法被调用,进而调用你重写的onTransact
方法
○onTransact
根据code
识别出是add
操作,从data
中解包参数
○ 调用本地实现的add(_arg0, _arg1)
方法
○ 将结果写入reply
返回:结果沿着原路返回,内核 Binder 驱动将数据拷贝回客户端进程
○ 客户端的transact()
方法返回
○Proxy.add
方法从_reply
中解包结果,并返回给客户端调用者
总结
概念 | 角色 | 关键点 |
---|---|---|
IBinder | 通信协议接口 | 定义了跨进程通信的基本操作(如 transact )。是所有 Binder 对象的根接口。代表一个远程对象的引用 |
Binder | 服务端基类 & 机制实现 | 实现了 IBinder 。作为服务端本地对象的基类(Stub ),处理请求(onTransact )。其内部类 BinderProxy 是客户端的远程代理 |
BinderProxy | 客户端代理 | 在客户端进程中,实现了 IBinder 。是 transact 调用的客户端起点,负责将调用序列化并发送给驱动 |
AIDL | 工具和胶水 | 自动生成代码(Stub , Proxy ),将复杂的 Binder /IBinder 操作封装成简单的 Java 接口调用,简化开发 |