一、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 通信中的位置和交互流程:

Android 核心服务 Binder 通信架构 应用进程 (App Process) Launcher 应用市场 普通 App Binder 代理 Binder 驱动 (Linux 内核) 数据序列化 → 跨进程传输 → 反序列化 → 方法分发 系统服务进程 (system_server) ActivityManagerService startActivity / bindService PackageManagerService installPackage / getInstalledApps 其他服务 WMS / WMS 等

五、Binder 通信的性能考量

在实际项目中,我总结了几条关于 Binder 通信的经验:

场景 建议 原因
频繁查询系统服务 批量获取 + 本地缓存 减少 Binder 调用次数
大数据传输 使用 FileDescriptor 共享 Binder 缓冲区只有 1MB
高频率调用 合并请求或降低频率 避免 Binder 线程池耗尽
回调接口 使用 oneway 关键字 避免阻塞调用方

总结一下:AMS 和 PMS 是 Android 系统里最核心的两个服务,它们的 Binder 通信模式代表了绝大多数系统服务的实现方式。理解了这个模式,你再看其他服务比如 WMS、WMS、InputManagerService,会发现都是类似的套路。

我个人建议:遇到跨进程通信问题时,先画一张 Binder 通信图,把客户端、服务端、Binder 驱动三个角色标清楚,问题往往就一目了然了。


公众号:蓝海资料掘金营,微信deep3321