Binder简介
Binder 是 Android 系统中最核心的进程间通信(IPC)机制。 它允许运行在不同进程中的应用程序组件(如 Activity 和 Service)安全、高效地相互调用方法、传递数据和发送消息。
我们可以把它想象成 Android 系统的“神经系统”,负责连接和协调各个不同的“器官”(进程)。
为什么需要 Binder?
进程隔离:出于安全性和稳定性的考虑,Android 中的每个应用都运行在自己独立的进程里,拥有自己独立的内存空间。一个应用不能直接访问另一个应用的内存。
通信需求:但应用之间又经常需要协作。例如:
一个应用需要调用系统服务(如获取位置信息、打开摄像头)。
你自己的应用需要与一个后台运行的 Service 通信。
两个不同的应用需要共享数据。
Binder 就是为了在 保证进程隔离 的前提下,安全高效地实现进程间通信 而诞生的。
Binder 的关键优势
高性能:相比传统的 Linux IPC(如管道、消息队列、Socket),Binder 只需要一次数据拷贝,效率非常高。这是它被选为 Android 核心机制的重要原因。
安全性:Binder 机制继承了进程本身的用户ID(UID)和进程ID(PID),从而可以方便地由系统进行身份校验和权限控制。系统服务可以知道是哪个进程发来的请求,并决定是否允许该操作。
面向对象:开发者感觉像是在调用本地对象的方法一样简单,所有复杂的序列化、反序列化和传输过程都被框架隐藏了。开发者通常通过 AIDL(Android Interface Definition Language) 来定义接口,让编译器自动生成上述的代理和实体代码。
AIDL简介
AIDL(Android Interface Definition Language)是一种接口定义语言。它的唯一目的就是为了让我们能够方便地利用Binder机制来实现跨进程通信(IPC)。
你可以把它看作一份双方都必须遵守的“合同”或“协议”。这份合同由你来起草,它明确规定了:
服务端能提供什么方法?(方法名)
客户端需要传递什么参数?(参数类型和方向:输入
in
、输出out
、输入输出inout
)服务端会返回什么结果?(返回值类型)
Android SDK工具会根据这份“合同”(.aidl
文件)自动生成隐藏的Java代码,这些代码封装了所有繁琐的Binder通信细节(如序列化、反序列化、收发数据等)。这样,开发者就可以专注于业务逻辑,而不必去手动实现复杂的Binder代理(Proxy)和存根(Stub)。aidl文件拥有自己的语法,不过和java语言比较类似。
为什么需要AIDL?
虽然Binder很强大,但如果要你手动编写所有用于Binder通信的代码(代理类、存根类、序列化/反序列化逻辑),那将是非常繁琐、容易出错且极其耗时的工作。
AIDL就是为了自动化这个过程而生的。你只需要定义好接口,Android构建系统就会为你生成所有“模板代码”。
应用层AIDL生成工具使用案例及源码分析
首先,在Android Studio中新建一个.aidl文件。
注意:高版本的Android Studio需要现在gradle中配置BuildFeature支持AIDL,否则将不能创建.aldl文件。
然后在生成的AIDL文件中写自己想定义的接口。
再点击Build,就会build目录下生成一个java格式的aidl文件。可以看到这个文件有100多行代码,而在我们的.aidl文件中只写了三行代码。这个AIDL生成工具还是十分好用的。
那我们来分析一下这个java文件。首先定义了一个默认的IMyAidlInterface接口,继承自所有binder接口的基类android.os.IInterface。然后在这个接口中拥有两个静态类Default和Stub。在Default类中是对接口方法的默认实现。在Stub类中,它的内部构造方法把这个接口对象和描述符相绑定。
Binder.java中的方法:public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) { mOwner = owner; mDescriptor = descriptor; }
继续分析这个Stub静态类,在这个类中有个静态方法asInterface。在这个方法中首先会调用Binder.java的queryLocalInterface方法来查询这个binder是否处于本进程中。如果存在的话就直接返回这个Stub本身。不存在的话则去创建一个Proxy静态类对象,Proxy会将调用打包,通过binder驱动发送给服务端。
Binder.java中的queryLocalInterface方法,可以看到就是获取之前传递的binder接口对象
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) { if (mDescriptor != null && mDescriptor.equals(descriptor)) { return mOwner; } return null; }
那么我们来看看这个静态类Proxy。在这个类中,会获取远程的Binder对象,在调用getServiceName时,并不是执行远程逻辑,而是将参数打包成Parcel然后通过binder远程发送给服务端,并且读取服务端的回复。在这个例子中,首先调用Parcel的obtain方法获取一个Parcel对象,然后写入数据,接下来调用transact方法将数据发送,然后服务端的onTransact方法会根据传递的事务ID调用子类重写的getServiceName方法,然后将数据返回,那么在Proxy中就能读取到返回值。
下面为AIDL工具生成的完整的java文件。
/*
* This file is auto-generated. DO NOT MODIFY.
* ... (AIDL 工具生成信息,说明了生成命令和参数)
*/
package com.example.myapplication.aidl;
// 这里可以导入非默认类型(例如自定义的 Parcelable 类型)
// import ...
// 定义 AIDL 接口。它继承自 android.os.IInterface,这是所有 Binder 接口的基类。
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Default implementation for IMyAidlInterface.
* 这是一个默认的接口实现,所有方法返回空或默认值。
* 通常用于服务端的本地实现,或者作为基类。在实际应用中,你会继承这个类并重写方法。
*/
public static class Default implements com.example.myapplication.aidl.IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
* AIDL 中定义的方法。这里只是一个示例实现,返回 null。
*/
@Override
public java.lang.String getServiceName(int serviceId) throws android.os.RemoteException {
return null; // 默认实现,返回 null
}
@Override
public android.os.IBinder asBinder() {
return null; // 默认实现,返回 null
}
}
/**
* Local-side IPC implementation stub class.
* 这是一个抽象的 Stub 类,继承自 Binder 并实现了 IMyAidlInterface。
* 这是服务端的基础实现类。服务端的实际实现通常会继承这个 Stub 类。
*/
public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IMyAidlInterface {
/**
* Binder 的唯一标识符,通常是接口的全限定名。
*/
public static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IMyAidlInterface";
/**
* 事务 ID,用于标识在 Binder 通信中调用的方法。
* 每个 AIDL 定义的方法都会有一个对应的 TRANSACTION_* 常量。
* 注意:这里的值必须与客户端 Proxy 类中的值保持一致。
*/
static final int TRANSACTION_getServiceName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
/** Construct the stub at attach it to the interface. */
@SuppressWarnings("this-escape")
public Stub() {
// 将接口描述符和当前实例关联到 Binder 对象。
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.myapplication.aidl.IMyAidlInterface interface,
* generating a proxy if needed.
* 这是一个工厂方法,用于将 IBinder 对象转换为 IMyAidlInterface 接口。
* 如果 IBinder 来自同一个进程(即本地调用),则直接返回 Stub 对象本身。
* 如果来自不同进程(远程调用),则返回一个封装了 IBinder 的 Proxy 对象。
*/
public static com.example.myapplication.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
// 查询本地接口,检查 IBinder 对象是否在当前进程。
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.myapplication.aidl.IMyAidlInterface))) {
// 如果在同一个进程,直接返回 Stub 对象。
return ((com.example.myapplication.aidl.IMyAidlInterface) iin);
}
// 如果在不同进程,返回一个代理对象(Proxy)。
return new com.example.myapplication.aidl.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this; // 返回当前 Binder 对象本身。
}
/**
* 处理跨进程调用的事务。
* 运行在服务端的 Binder 线程池中。
* @param code 事务 ID,标识要调用的方法(例如 TRANSACTION_getServiceName)。
* @param data 包含调用参数的 Parcel 对象。
* @param reply 用于写入返回值的 Parcel 对象。
* @param flags 操作标志,0 表示普通 RPC。
* @return 返回 true 表示事务处理成功,false 表示未知事务。
*/
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
// 确保数据包中包含正确的接口描述符
if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
data.enforceInterface(descriptor);
}
if (code == INTERFACE_TRANSACTION) { // 查询接口描述符的事务
reply.writeString(descriptor);
return true;
}
// 根据事务 ID 分发到具体的方法
switch (code) {
case TRANSACTION_getServiceName: {
// 读取参数
int _arg0;
_arg0 = data.readInt();
// 调用实际的方法实现(子类重写的方法)
java.lang.String _result = this.getServiceName(_arg0);
reply.writeNoException(); // 写入无异常标志
reply.writeString(_result); // 写入返回值
break;
}
default: {
// 未知事务,交由父类处理
return super.onTransact(code, data, reply, flags);
}
}
return true; // 事务处理完成
}
/**
* 代理类 (Proxy),用于客户端。
* 当客户端和服务端不在同一进程时,客户端拿到的接口对象实际上是这个 Proxy 的实例。
* Proxy 会将方法调用打包,通过 Binder 驱动发送给服务端。
*/
private static class Proxy implements com.example.myapplication.aidl.IMyAidlInterface {
private android.os.IBinder mRemote; // 远程 Binder 对象的引用
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* 客户端调用的方法。它并不真正执行逻辑,而是将参数打包,通过 Binder 发送事务。
*/
@Override
public java.lang.String getServiceName(int serviceId) throws android.os.RemoteException {
// 准备输入和输出的 Parcel 对象
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR); // 写入接口描述符
_data.writeInt(serviceId); // 写入参数
// 通过 Binder 发送事务,并等待回复
// TRANSACTION_getServiceName 是事务 ID,必须与 Stub 中定义的一致
// _data 是发送的数据
// _reply 是接收回复的数据
// 0 是标志位,通常为 0
boolean _status = mRemote.transact(Stub.TRANSACTION_getServiceName, _data, _reply, 0);
// 读取回复中的异常(如果有的话,会抛出 RemoteException)
_reply.readException();
// 读取返回值
_result = _reply.readString();
} finally {
// 回收 Parcel 对象,避免内存泄漏
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
* 这是 AIDL 文件中定义的方法声明。
* 实际实现由 Stub 的子类(服务端)或 Proxy(客户端)完成。
*/
public java.lang.String getServiceName(int serviceId) throws android.os.RemoteException;
}
从transact到onTransact
继续分析一下transact的源码和onTransact的源码。在transact方法中,会调用本地的onTransact方法,然后执行重写的onTransact的业务逻辑。
/**
* 执行一个Binder事务。
* 这是Binder通信的核心方法,用于向Binder对象发送请求并可选地接收回复。
*
* @param code 事务标识符,指明要执行的操作。由接口定义(如AIDL中的TRANSACTION_*)。
* @param data 包含调用参数的Parcel对象。不能为null。
* @param reply 用于接收返回值的Parcel对象。如果为null,表示这是一个单向调用(oneway)。
* @param flags 额外的操作标志。0表示普通RPC,FLAG_ONEWAY表示单向调用。
* @return 返回true表示事务成功完成,false通常表示未能识别事务代码。
* @throws RemoteException 如果事务执行过程中发生错误。
*/
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
// 调试日志,通常在发布版本中 if (false) 会被编译器优化掉
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
// 确保数据 parcel 的读取位置重置到起始处,这是一个安全措施
// 因为传入的data可能已经被写入数据,我们需要从开头读取
if (data != null) {
data.setDataPosition(0);
}
// 关键调用:将事务派发给 onTransact 方法处理
// 注意:这里调用的是 this.onTransact,对于本地Binder对象,这就是最终处理点
// 对于远程代理,这个transact方法会被重写,通过Binder驱动发送到远程进程
boolean r = onTransact(code, data, reply, flags);
// 如果存在回复 parcel,将其读取位置重置到起始处,方便调用者读取结果
if (reply != null) {
reply.setDataPosition(0);
}
// 返回事务执行结果
return r;
}
/**
* 处理一个传入的Binder事务。
* 这是一个默认的存根实现,仅返回false。你需要重写此方法来实现具体的事务解组逻辑。
*
* <p>如果你想调用这个方法,应该使用 transact()。
*
* <p>实现者在返回结果时通常应该使用
* {@link Parcel#writeNoException() Parcel.writeNoException} 和
* {@link Parcel#writeException(Exception) Parcel.writeException}
* 来将异常传播回调用者。
*
* @param code 要执行的操作。应该是 {@link #FIRST_CALL_TRANSACTION} 和
* {@link #LAST_CALL_TRANSACTION} 之间的数字。
* @param data 从调用者接收到的已组编的数据。
* @param reply 如果调用者期望返回结果,应该将结果组编到这里。
* @param flags 额外的操作标志。0表示普通RPC,或 {@link #FLAG_ONEWAY} 表示单向RPC。
* @return 成功调用返回true;返回false通常表示未能识别事务代码。
*/
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
// 处理接口描述符查询事务
if (code == INTERFACE_TRANSACTION) {
reply.writeString(getInterfaceDescriptor());
return true;
}
// 处理dump诊断事务
else if (code == DUMP_TRANSACTION) {
ParcelFileDescriptor fd = data.readFileDescriptor();
String[] args = data.readStringArray();
if (fd != null) {
try {
// 执行dump操作,通常用于输出调试信息
dump(fd.getFileDescriptor(), args);
} finally {
IoUtils.closeQuietly(fd);
}
}
// 写入StrictMode头部信息
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
return true;
}
// 处理shell命令事务
else if (code == SHELL_COMMAND_TRANSACTION) {
ParcelFileDescriptor in = data.readFileDescriptor();
ParcelFileDescriptor out = data.readFileDescriptor();
ParcelFileDescriptor err = data.readFileDescriptor();
String[] args = data.readStringArray();
ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
try {
if (out != null) {
// 执行shell命令
shellCommand(in != null ? in.getFileDescriptor() : null,
out.getFileDescriptor(),
err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
args, shellCallback, resultReceiver);
}
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(err);
// 写入StrictMode头部信息
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
}
return true;
}
// 默认返回false,表示不识别此事务代码
// 子类应该重写此方法以处理自定义事务
return false;
}