一、Binder 在应用层通信中的角色
说实话,Binder 机制在 Android 里到底有多重要?
我经常跟团队里的新人说:你手机里每一个 App 的启动、每一个应用的安装、每一个权限的申请,背后都离不开 Binder 通信。AMS、PMS 这些核心服务,说白了就是通过 Binder 暴露给应用层调用的。
我个人习惯把 Binder 在应用层的通信模式总结成一句话:客户端拿代理,服务端接请求,内核帮忙传数据。今天我们就拿两个最典型的服务——ActivityManagerService 和 PackageManagerService——来拆解这个流程。
核心要点:应用层通过 Binder 调用系统服务,本质上是跨进程方法调用。客户端拿到的是服务端的代理对象,调用代理方法时,Binder 驱动负责把参数序列化、跨进程传输、反序列化,最终执行服务端方法。
二、ActivityManagerService 的 Binder 通信实例
2.1 从 startActivity 说起
你想想看,当你在 Launcher 上点一个图标,系统是怎么知道要启动哪个 Activity 的?
流程其实不复杂:Launcher 进程通过 Binder 调用 AMS 的 startActivity 方法。AMS 运行在 system_server 进程里,Launcher 是单独的 App 进程。这两个进程怎么通信?靠 Binder。
我截取一段关键代码,你看看就明白了:
// Launcher 进程中的调用
// 实际上是通过 Instrumentation.execStartActivity()
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// 获取 AMS 的 Binder 代理
IActivityManager am = ActivityManager.getService();
// 跨进程调用 startActivity
int result = am.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
// ...
}
这里有个细节:ActivityManager.getService() 返回的是什么?
嗯,它返回的是 IActivityManager 接口的 Binder 代理对象。这个代理对象内部封装了 mRemote.transact() 调用。说白了,你调 startActivity,最终会走到 Binder 驱动,把数据打包发给 system_server 进程里的 AMS。
2.2 AMS 端的处理
AMS 端是怎么接收的?它实现了 IActivityManager.Stub 抽象类,重写了 onTransact() 方法。Binder 驱动把请求分发过来后,AMS 根据 code 值判断调用哪个方法。
// AMS 继承自 IActivityManager.Stub
public class ActivityManagerService extends IActivityManager.Stub {
@Override
public int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
// 检查调用者权限
enforceNotIsolatedCaller("startActivity");
// 这里才是真正的 Activity 启动逻辑
// 包括任务栈管理、生命周期调度等
return startActivityAsUser(caller, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
profilerInfo, options, UserHandle.getCallingUserId());
}
}
避坑指南:我曾经在调试一个 ANR 问题时,发现是 Launcher 频繁调用 AMS 的 startActivity 导致 Binder 线程池耗尽。后来加了频率限制才解决。记住:Binder 线程池默认只有 16 个线程,别把 AMS 当普通 API 随便调。
三、PackageManagerService 的 Binder 通信实例
3.1 应用安装与查询
PackageManagerService 是另一个高频使用的系统服务。你每次调用 getPackageManager() 获取已安装应用列表,其实都是在走 Binder 通信。
来看一个实际场景:应用市场安装 App 时,Installer 进程会调用 PMS 的 installPackage 方法。
// 应用市场进程中的调用
public class PackageInstaller {
public void install(Uri packageUri, IPackageInstallObserver observer, int flags) {
// 获取 PMS 的 Binder 代理
IPackageManager pm = AppGlobals.getPackageManager();
// 跨进程调用安装方法
pm.installPackage(packageUri, observer, flags,
mContext.getPackageName());
}
}
PMS 端收到请求后,会做一系列检查:签名验证、权限检查、存储空间判断等等。这些操作都在 system_server 进程里完成,不会阻塞调用方进程。
3.2 查询应用信息
还有一个很常见的场景:获取已安装应用列表。
// 普通 App 中获取已安装应用
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> installedApps =
pm.getInstalledApplications(PackageManager.GET_META_DATA);
// 内部实现:通过 Binder 调用 PMS
// PackageManager 的 getInstalledApplications 最终会调用
// PMS 的 getInstalledApplications 方法
这里有个性能问题值得注意:每次查询都会走一次完整的 Binder 调用。如果频繁查询,比如在列表滑动时不断刷新,Binder 通信的开销就会累积。
我曾经踩过的坑:有个项目在首页同时查询了 5 次应用信息,每次都是独立的 Binder 调用。结果在低端机上明显卡顿。后来改成一次查询全部信息,本地缓存,性能提升很明显。
四、核心服务的 Binder 通信架构图
下面这张图展示了 AMS 和 PMS 在 Binder 通信中的位置和交互流程:
五、Binder 通信的性能考量
在实际项目中,我总结了几条关于 Binder 通信的经验:
| 场景 | 建议 | 原因 |
|---|---|---|
| 频繁查询系统服务 | 批量获取 + 本地缓存 | 减少 Binder 调用次数 |
| 大数据传输 | 使用 FileDescriptor 共享 | Binder 缓冲区只有 1MB |
| 高频率调用 | 合并请求或降低频率 | 避免 Binder 线程池耗尽 |
| 回调接口 | 使用 oneway 关键字 | 避免阻塞调用方 |
总结一下:AMS 和 PMS 是 Android 系统里最核心的两个服务,它们的 Binder 通信模式代表了绝大多数系统服务的实现方式。理解了这个模式,你再看其他服务比如 WMS、WMS、InputManagerService,会发现都是类似的套路。
我个人建议:遇到跨进程通信问题时,先画一张 Binder 通信图,把客户端、服务端、Binder 驱动三个角色标清楚,问题往往就一目了然了。