1. WMS 与 Input 系统概述:理解 WindowManagerService 和 Input 系统的角色与关系

做 Android 系统优化这些年,我碰到最多的性能问题,十有八九都能追溯到两个地方——WindowManagerService(WMS)和Input 系统。说白了,一个管窗口怎么摆,一个管触摸怎么传,但一旦它们配合不好,用户就感觉“卡顿”“点不上”“反应慢”。想优化响应,必须先把这两个角色的分工和协作机制搞清楚。

1.1 WindowManagerService:窗口的“总调度员”

WMS 是 Android 框架层里最核心的服务之一,运行在 system_server 进程里。它的本职工作很简单:管理所有窗口的创建、顺序、大小、动画和焦点。每次你打开一个 Activity、弹出对话框、显示 Toast,背后都是 WMS 在安排窗口的叠放次序。

举个例子,我去年优化一个多窗口应用时,发现切换任务卡了 200ms。一查,WMS 在做窗口重排序时占了 80ms。问题出在哪?它每收到一次窗口变化,就要遍历整个窗口链表并重新计算 z-order。嗯,这在窗口少的时候没问题,但一旦超过 30 个,就开始卡了。

WMS 的主要职责可以归纳成这样:

  • 窗口管理:addWindow、removeWindow、relayoutWindow
  • 焦点管理:谁拥有输入焦点(Focus)
  • 输入法交互:调整窗口避开软键盘
  • 动画与过渡:启动窗口动画、窗口缩略图
  • 转场控制:多窗口切换时的表面合成

1.2 Input 系统:从手指到屏幕的“快递员”

Input 系统则负责把用户的触摸、按键事件,从硬件设备一路送到对应的窗口。它包括内核里的 InputReader 和 InputDispatcher 两个主要线程,以及框架层的 InputManagerService(IMS)。

我记得以前有个线上 bug:用户快速滑动列表时,偶尔会“跳帧”。抓到日志发现,InputDispatcher 分发事件的间隔不均匀,有时候两个事件之间隔了 30ms,有时候却只有 2ms。后来定位到原因是 WMS 在调整窗口时,临时抢占了 InputDispatcher 需要的锁——说白了就是资源竞争。

Input 系统的关键流程:

  1. 内核驱动读取原始触摸数据
  2. InputReader 解析成 RawInputEvent
  3. InputDispatcher 查找焦点窗口
  4. 通过 SocketPair 发送 MotionEvent 到应用进程
  5. 应用主线程处理并反馈

1.3 它们的关系:互相依赖,也互相掣肘

WMS 管“窗口去哪儿”,Input 管“事件去哪儿”。两者通过一个关键机制连接——焦点窗口查询。当 InputDispatcher 收到一个触摸事件,它必须问 WMS:“当前触摸点下面是谁的窗口?” WMS 根据窗口的 hit-test 区域返回窗口 ,InputDispatcher 才会把事件发过去。

看起来简单,对吧?但实际中,这两个系统运行在同一个进程(system_server)里,共享着大量锁。一旦某个环节变慢——比如 WMS 正在执行一个耗时的 relayout——它就会阻塞 InputDispatcher 的查询请求。这就是很多“触摸无响应”问题的根源。

核心矛盾: WMS 需要保证窗口状态的正确性(可能要排队执行任务),而 Input 系统需要极低延迟地将事件送达窗口。当两者竞争锁或线程资源时,响应就恶化了。

我建议你在做优化前,先把这张图记在脑子里:

WMS 与 Input 系统协作关系 内核层 框架层 (system_server) 应用层 应用A 应用B WindowManagerService 窗口管理·焦点·动画 InputManagerService 输入分发·策略 SurfaceFlinger 合成与渲染 InputReader 线程 InputDispatcher 线程 关键交互点:InputDispatcher 通过查询 WMS 获得当前触摸位置的窗口 ,然后直送应用。

1.4 一张表理清分工

维度 WMS Input 系统
核心职责 窗口布局、焦点、动画 事件读取、分发、策略
运行线程 UI 线程 + WMS 内部 Handler InputReader / InputDispatcher 两个线程
主要耗时操作 relayout、动画回调、焦点切换 事件解析、窗口命中检测、Socket写入
与性能相关的常见问题 窗口同步锁导致 ANR,密集窗口操作卡顿 事件分发延迟,丢帧,焦点命中失败

1.5 为什么要把它们放在一起优化?

你可能觉得,WMS 和 Input 是两套独立的逻辑,各自保各自不就行了吗?我在实际项目中踩过的坑告诉我:它们之间有一条隐形的“快速通道”。任何一个环节出现瓶颈,都会通过锁或者消息队列传染给另一方。

经验之谈: 我曾遇到一个 case,用户点按 Home 键后桌面图标延迟了 400ms 才响应。最终根因是 WMS 在处理一个窗口动画时持有了内部锁,而 InputDispatcher 查询焦点时必须等待同一把锁释放。解决方案是把焦点查询提前到动画之前做 —— 代价只是多一次双向 binder 调用,但响应提升了 70%。

所以,想要做好响应优化,不能只盯着 WMS 或者只盯着 Input。你得把它们看作一个协作体:

  • WMS 尽量缩短窗口更新的临界区
  • Input 系统尽量提前获取窗口焦点布局信息(比如通过缓存)
  • 表面合成环节(SurfaceFlinger)也要配合,减少 VSYNC 漂移

嗯,到了这里你应该明白了。这一章就是搭个骨架,后面的章节我们会逐层深入:从 WMS 的窗口排序算法,到 Input 的调度策略,再到实战中的代码级优化。你想想看,如果能在这两个系统之间画出清晰的协作流程,很多疑难杂症就不再神秘了。

注意: 不要试图在优化阶段绕过 WMS 直接操作 Input 分发路径——这破坏了系统契约,容易导致窗口泄露或焦点混乱。我见过有人为了“提速”直接给 InputDispatcher 打补丁,结果多窗口模式下频频崩溃。

最后留一个思考点:当你发现某个场景下触摸延迟明显增加时,优先查一下 WMS 当前是否有窗口正在 relayout 或动画。那往往是 Input 事件被阻塞的第一个信号。