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 进程 Activity / Window ViewRootImpl BinderProxy Binder 驱动 内核空间 事务路由 数据拷贝 system_server AMS / WMS Binder 实体 线程池处理 回调 (IWindow) 通信流程说明 1. App 进程通过 BinderProxy 发起请求 2. Binder 驱动将数据从用户空间拷贝到内核空间 3. system_server 的 Binder 线程池接收并处理请求 4. AMS/WMS 执行业务逻辑,返回结果或发起回调 5. 回调通过 IWindow 接口回到 App 进程的 ViewRootImpl

实际项目中的坑与经验

我在做系统稳定性优化时,遇到过一个问题:某个三方 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