Binder 的基本原理
网上有很多介绍Binder的文章,这里不过多介绍,
首先要明确一点 Binder 是一个 RPC(Remote Procedure Call) 框架,也就是说借助于 Binder,我们可以在 A 进程中访问 B 进程中的函数
RPC 一般基于 IPC(Inter-Process Communication) 来实现的,IPC 就是跨进程数据传输(通信)
Binder 的 RPC 是如何实现的:
一般来说,A 进程访问 B 进程函数,我们需要:
-
在 A 进程中按照固定的规则打包数据,这些数据包含了:
- 数据发给那个进程,Binder 中是一个整型变量 Handle
- 要调用目标进程中的那个函数,Binder 中用一个整型变量 Code 表示
- 目标函数的参数
- 要执行具体什么操作,也就是 Binder 协议
-
进程 B 收到数据,按照固定的格式解析出数据,调用函数,并使用相同的格式将函数的返回值传递给进程 A。
Binder 应用层工作流程
Binder 是一个 RPC(Remote Procedure Call) 框架,翻译成中文就是远程过程调用。也就是说通过 Binder:
- 可以在 A 进程中访问 B 进程中定义的函数
- 进程 B 中的这些等待着被远程调用的函数的集合,我们称其为 Binder 服务(Binder Service)
- 进程 A 称之为
Binder 客户端(Binder Client),进程 B 称之为Binder 服务端(Binder Server) - 通常,系统中的服务很多,我们需要一个管家来管理它们,
服务管家(ServiceManager)是 Android 系统启动时,启动的一个用于管理 Binder 服务(Binder Service) 的进程。通常,服务(Service) 需要事先注册到服务管家(ServiceManager) ,其他进程向服务管家(ServiceManager) 查询服务后才能使用服务。 - Binder 的 RPC 能力通过 **
Binder 驱动**实现
通常一个完整的 Binder 程序涉及 4 个流程:
- 在 Binder Server 端定义好服务
- 然后向 ServiceManager 注册服务
- 在 Binder Client 中向 ServiceManager 获取到服务
- 发起远程调用,调用 Binder Server 中定义好的服务
学习与参考: 002.Binder 基本原理 | Ahao's Technical Blog
AIDL(Android Interface Definition Language)
AIDL(安卓接口定义语言) 是 Android 中最常用的跨进程通信机制,主要用于应用层进程间的通信。
主要特点
- 应用层IPC:主要用于应用之间的通信
- 基于Binder:底层使用 Binder 机制
- 支持复杂数据类型:基本类型、String、List、Map、Parcelable 对象等
- 同步/异步调用:默认同步,可通过 oneway 实现异步
Android的AIDL(Android接口定义语言)是一套完整的跨进程通信API体系。它不仅支持基础数据类型和接口回调,还提供了同步/异步调用、权限控制、服务管理等一系列核心功能。
核心API与关键概念
下表为你梳理了AIDL的核心功能模块:
| 模块 | 主要API/概念 | 说明与用途 |
|---|---|---|
| 1. 接口定义 | AIDL文件 (.aidl) | 使用AIDL语法声明跨进程的接口契约。 |
| 2. 核心通信对象 | IBinder | 所有IPC通信的底层句柄。Service.onBind返回它。 |
IInterface | 所有AIDL接口的根基,定义了与IBinder交互的能力。 | |
Binder 类 | 服务端的基类,实现了IBinder,用于处理远程调用。 | |
Stub 类 (抽象类) | 由AIDL编译器生成的服务器存根,继承自Binder并实现主接口。 | |
Proxy 类 (内部类) | 由AIDL编译器生成的客户端代理,封装了跨进程调用序列化逻辑。 | |
| 3. 类型系统 | 基本类型 | int, long, char, boolean, double, byte, float。 |
String | 字符串,支持UTF-8编码。 | |
CharSequence | 可直接传递SpannableString等。 | |
Parcelable | 通过实现该接口,自定义可序列化的复杂对象。 | |
List / Map | 集合类型,其元素必须为AIDL支持的类型。 | |
| 定向Tag | in(输入), out(输出), inout(双向),修饰非基本类型参数。 | |
| 4. 接口回调 | RemoteCallbackList | 用于在服务端安全地管理并回调多个客户端监听器。 |
| 5. 调用模式 | 同步调用 | 默认模式,客户端线程会阻塞直至服务端返回。 |
| 异步调用 | 使用oneway关键字修饰接口方法,调用不阻塞。 | |
| 6. 异常处理 | RemoteException | 所有跨进程调用都可能抛出的根异常,如进程死亡、通信失败等。 |
SecurityException | 绑定服务或调用时,权限校验失败会抛出。 | |
| 7. 服务绑定 | Service | 承载AIDL接口的服务组件。 |
ServiceConnection | 客户端用于监听与服务的连接状态。 | |
bindService() / unbindService() | 绑定与解绑服务的方法。 | |
Context.BIND_AUTO_CREATE | 绑定标志,服务不存在时自动创建。 |
💻 深度解析与最佳实践
1. 通信核心:Binder、Stub与Proxy
这是AIDL工作的基石。服务端实现 Stub,客户端通过 Proxy 调用,两者通过Binder驱动进行数据交换。
// 服务端实现
public class MyService extends Service {
private final IMyService.Stub binder = new IMyService.Stub() {
@Override
public int calculate(int a, int b) throws RemoteException {
return a + b;
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
// 客户端调用
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 关键:将IBinder转换为接口代理对象
IMyService myService = IMyService.Stub.asInterface(service);
myService.calculate(5, 3);
}
};
2. 高级数据类型与方向Tag
Parcelable 是实现复杂数据传输的关键。
// 1. 定义 Parcelable 数据类
public class User implements Parcelable {
public String name;
public int id;
// ... 必须实现 writeToParcel, describeContents, CREATOR
}
// 2. 为 Parcelable 创建对应的 .aidl 文件 (User.aidl)
// parcelable User;
// 3. 在接口AIDL中引用
// import com.example.User; // 显式导入
// void updateUser(in User user);
方向Tag (in, out, inout) 直接影响性能和逻辑:
in:数据从客户端流向服务端,服务端修改不会影响客户端对象。out:对象从服务端“返回”给客户端。客户端传入的对象字段不会被发送,但服务端可以创建或修改对象并传回。inout:双向传输,开销最大,应谨慎使用。 方向修饰符(仅对 “非基本类型 & 非 String/CharSequence” 参数生效)
void printUser(in User u); // 客户端把 User 序列化传过来
void fillUser(out User u); // 服务端 new 一个 User 写回去
void updateUser(inout User u); // 两端都可改
3. 管理接口回调:RemoteCallbackList
在服务端,不能直接保存客户端传来的 IBinder 回调接口,因为Binder对象可能因进程死亡而失效。RemoteCallbackList 能自动管理这种生命周期。
public class MyService extends Service {
private final RemoteCallbackList<ICallback> callbackList = new RemoteCallbackList<>();
private final IMyService.Stub binder = new IMyService.Stub() {
@Override
public void registerCallback(ICallback cb) {
callbackList.register(cb);
}
@Override
public void unregisterCallback(ICallback cb) {
callbackList.unregister(cb);
}
};
// 向所有客户端广播事件
private void broadcastEvent(String event) {
int n = callbackList.beginBroadcast();
for (int i = 0; i < n; i++) {
try {
callbackList.getBroadcastItem(i).onEvent(event);
} catch (RemoteException e) {
// 客户端可能已死亡,忽略
}
}
callbackList.finishBroadcast();
}
}
4. 同步 (two-way) 与异步 (oneway)
- 同步:客户端线程会阻塞,适用于需要立即结果的调用。
- 异步:在接口方法前加
oneway关键字,不等待返回,也没有返回值。适用于通知型操作,能有效避免客户端阻塞,但需注意其“至多一次 (at-most-once)”的语义,调用可能因进程死亡而丢失。
// 同步方法
int calculate(in int a, in int b);
// 异步方法(无返回值,不抛RemoteException)
oneway void notifySomethingHappened(in String event);
5. 权限控制与安全
可在 AndroidManifest.xml 中为服务声明自定义权限,并在 onBind 或具体方法内校验。
<service
android:name=".MyService"
android:permission="com.example.MY_PERMISSION"
android:exported="true"> <!-- 允许其他应用绑定 -->
</service>
// 在服务端的onBind或方法实现中校验
public IBinder onBind(Intent intent) {
if (checkCallingOrSelfPermission("com.example.MY_PERMISSION")
!= PackageManager.PERMISSION_GRANTED) {
return null; // 绑定失败
}
return binder;
}
🎯 性能与稳定性最佳实践
- 减少跨进程调用次数:设计接口时,尽量将多个操作合并为一次调用(如传递一个
User对象,而非多次调用设置name、id)。 - 注意线程模型:AIDL调用在服务端默认运行在 Binder线程池 中(非主线程),因此需要进行线程同步。使用
oneway调用时,服务端方法仍会在Binder线程池中执行。 - 妥善处理
RemoteException:所有跨进程调用都必须捕获此异常,它意味着连接已断开。 - 及时解绑:在客户端(如
Activity)的onDestroy中调用unbindService,防止内存泄漏。 - 接口版本兼容:为已发布的AIDL接口添加新方法时,考虑向后兼容,例如使用默认实现或版本号协商。
Demo 示例
IRemoteService.aidl
package com.example.aidl;
import com.example.aidl.User; // 自定义 Parcelable
interface IRemoteService {
int add(int a, int b); // 基本类型
void printUser(in User u); // in 方向
void getUser(out User u); // out 方向
oneway void fireCallback(); // 非阻塞
}
User.aidl
package com.example.aidl;
parcelable User;
服务端实现
public class RemoteService extends Service {
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int add(int a, int b) { return a + b; }
public void printUser(User u) { Log.d("AIDL", u.name); }
public void getUser(User u) {
u.name = "server"; u.age = 18; // 填充 out 参数
}
public void fireCallback() { /* 单向,无返回 */ }
};
public IBinder onBind(Intent i) { return binder; }
}
客户端调用
IRemoteService s = IRemoteService.Stub.asInterface(service);
int r = s.add(1, 2);