47. 应用层 Binder:ActivityManagerService、WindowManagerService 等核心服务的 Binder 通信
说到 Android 系统里的 Binder 应用,绕不开的就是 AMS 和 WMS 这两大巨头。我刚开始啃这部分源码时,说实话有点懵——怎么一个 Activity 启动,牵扯出那么多跨进程调用?后来理清了 Binder 这条线,才恍然大悟。
说白了,AMS(ActivityManagerService)和 WMS(WindowManagerService)都是运行在 system_server 进程里的核心服务。它们通过 Binder 机制,向各个 App 进程暴露接口。App 进程想干点什么事,比如启动一个 Activity、调整窗口大小,都得通过 Binder 把请求发给这些服务。
AMS 的 Binder 通信架构
AMS 本身继承自 IActivityManager.Stub,这是一个典型的 Binder 服务端实现。客户端拿到的是 IActivityManager 接口的代理对象,调用方法时,参数会被序列化到 Parcel 里,跨进程传输到 system_server 端。
我举个例子,你平时调用 startActivity 时,流程是这样的:
// App 进程调用
Instrumentation.execStartActivity()
→ ActivityManager.getService().startActivity()
→ Binder 驱动传输
→ AMS.startActivity() // system_server 进程
这里有个关键点:ActivityManager.getService() 返回的其实是一个 BinderProxy 对象。它内部持有对 AMS 服务端 Binder 实体的引用。每次调用,都是一次完整的 Binder 事务。
核心要点:AMS 的 Binder 接口定义在 IActivityManager.aidl 中。所有跨进程方法调用,最终都会走到 AMS 的 onTransact() 方法里,根据 code 分发到对应的业务逻辑。
WMS 的 Binder 通信特点
WMS 和 AMS 类似,也是通过 IWindowManager.aidl 暴露接口。但 WMS 有一个独特的地方——它需要频繁地和 App 进程的 ViewRootImpl 通信,处理窗口的添加、更新、移除等操作。
我记得有一次排查窗口闪烁问题,发现是 WMS 返回的 SurfaceControl 对象在跨进程传递时出了问题。嗯,这里要注意:WMS 和 App 之间的通信是双向的。App 通过 Binder 向 WMS 请求窗口操作,WMS 也会通过 IWindow 接口回调 App 进程。
// App 进程向 WMS 添加窗口
WindowManagerGlobal.addView()
→ WMS.openSession() // 获取 Session
→ Session.addToDisplay()
→ WMS.addWindow()
这个 Session 对象本身也是一个 Binder 实体。每个 App 进程和 WMS 之间,都维护着一个 Session 通道。窗口相关的操作,大部分都走这个通道。
核心服务的 Binder 线程模型
AMS 和 WMS 都运行在 system_server 进程里。这个进程的 Binder 线程池默认有 16 个线程。我建议你注意一下:如果某个 Binder 调用耗时太长,会阻塞整个线程池,导致其他服务响应变慢。
| 服务 | Binder 接口文件 | 主要客户端 | 典型调用频率 |
|---|---|---|---|
| AMS | IActivityManager.aidl | 所有 App 进程 | 高(Activity 生命周期) |
| WMS | IWindowManager.aidl | 所有 App 进程 | 高(窗口刷新) |
| PMS | IPackageManager.aidl | 安装器、App | 中(安装查询) |
| SurfaceFlinger | ISurfaceComposer.aidl | WMS、App | 极高(每帧) |
避坑指南:我曾经在优化启动速度时,发现 AMS 的 Binder 调用在系统负载高时会出现明显延迟。后来加了异步 Binder 调用,把一些非关键路径的请求放到后台线程处理,效果立竿见影。
Binder 通信中的对象传递
AMS 和 WMS 经常需要传递复杂对象,比如 Intent、ActivityRecord、WindowState 等。这些对象要么实现 Parcelable 接口,要么本身就是 Binder 实体。
你想想看,一个 Intent 从 App 进程传到 AMS,里面可能包含 Bundle 数据。Bundle 内部又可能包含 Binder 对象。这一层层序列化,其实挺考验性能的。我个人习惯是在传递大数据时,尽量用文件描述符或者共享内存,减少数据拷贝。
SVG 流程图:AMS/WMS Binder 通信全景
实际项目中的坑与经验
我在做系统稳定性优化时,遇到过一个问题:某个三方 App 频繁调用 AMS 的 Binder 接口,导致 system_server 的 Binder 线程全部被占满。其他 App 的启动请求被阻塞,用户感觉手机卡死。
排查下来,发现是那个 App 在短时间内发起了大量 getTasks() 调用。这个接口内部会遍历所有 Activity 栈,耗时较长。解决方案是在 AMS 侧加了频率限制,同时建议客户端改用缓存机制。
注意:不要在主线程做耗时的 Binder 调用。AMS 和 WMS 的某些接口(比如 getRecentTasks())内部会持有锁,如果主线程直接调用,可能引发 ANR。我建议所有 Binder 调用都走异步线程,或者用 Handler 做线程切换。
总结一下
AMS 和 WMS 的 Binder 通信,本质上就是典型的 C/S 架构。服务端在 system_server 里注册 Binder 实体,客户端通过 ServiceManager 获取代理,然后发起跨进程调用。这里面有几个关键点:
- 接口定义在 AIDL 文件中,自动生成 Stub 和 Proxy 类
- 复杂对象需要实现 Parcelable 接口
- Binder 线程池的容量直接影响系统响应速度
- 回调机制让通信变成双向的
嗯,这部分内容其实不难,关键是理解 Binder 在整个 Android 系统里扮演的角色。你只要把 AMS 和 WMS 的通信流程走一遍,其他服务的 Binder 实现基本大同小异。
公众号:蓝海资料掘金营,微信deep3321