3. WindowManagerService 消息队列深度排查

窗口动画卡顿、触摸事件响应延迟、界面冻结——这些问题往往与 WindowManagerService 内部消息队列的健康状态直接相关。WMS 依赖其主线程的 Handler 队列处理窗口布局、动画更新、输入事件派发等核心任务。一旦队列出现积压、任务死锁或线程阻塞,便会引发连锁的用户感知异常。

3.1 现象识别

以下是窗口动画卡顿与输入事件队列异常的典型表现,有助于工程师快速建立排查方向。

现象分类用户感知系统信号
窗口动画卡顿启动/退出动画掉帧,过渡动画不连贯,App 窗口突然“卡住”后跳帧dumpsys window traces 显示动画帧率低于 60fps;ANR 日志中出现 Executing service com.android.systemui/.SystemUIService
输入事件队列异常触摸/按键无响应,点击后经过数秒才生效,或触控轨迹明显滞后dumpsys input 显示 Dispatch time too far in the future 或 Window input channel BUFFERED
死锁判断整个 SystemUI 或 Launcher 僵死,logcat 中无输入事件派发日志,dumpsys window 命令超时无响应dumpsys -t 5 window 卡住;cat /proc/<pid>/task/*/stack 显示多个线程相互等待锁

3.2 数据采集

使用 dumpsys window 命令获取队列状态时,建议优先执行以下三条命令,分别对应不同维度。

# 获取 WMS 消息队列整体信息(包含 pending 和 delayed 任务数)
adb shell dumpsys window | grep -A 20 "Looper"
# 获取详细的动画帧队列状态
adb shell dumpsys window animations
# 获取输入通道绑定窗口的队列深度
adb shell dumpsys input --windows
数据采集关键点:在故障复现期间连续采集 3~5 次(间隔 1 秒),对比队列深度变化趋势。单次快照只能判断是否阻塞,无法识别队列是否在持续增长。

3.3 原因定位

以下故障树展示了从“窗口动画卡顿”根节点出发的排查路径。每个叶子节点对应一个可执行的检查点。

故障树:WMS 消息队列卡顿原因定位

WMS消息队列卡顿故障树 窗口动画卡顿 WMS Handler 消息积压 输入事件队列异常 窗口布局死循环 动画帧任务阻塞 InputChannel 满 触摸事件派发 被场景动画阻塞 dumpsys window Looper 队列 >2000 AnimationHandler runNextFrame 超时 input dispatch 队列满 死锁判断 stack 分析 排查规则:先检查 Looper 整体压入速度,再分离窗口动画与输入事件两个子队列。 若 Looper 队列深度持续大于 500,优先定位 WMS Handler 中的死循环或阻塞 Binder 调用。

3.4 修复动作

根据故障树定位到的叶子节点,执行以下针对性修复动作。

定位结果修复动作验证方式
Looper 队列深度 > 2000,且持续增长检查 WMS Handler 是否在 performLayoutAndPlaceSurfacesLocked 中频繁调用同步 Binder 请求(如 reloadWindowResources),将其改为异步或移除冗余触发。复测后队列深度降至 200 以下
AnimationHandler 的 runNextFrame 超时减少单帧内的 SurfaceTransaction 数量,避免多个动画同时触发 setAlpha/setMatrix 导致 VSYNC 延迟。dumpsys window animations 显示帧间隔 < 16ms
Input 通道队列满优化对应窗口的 UI 线程消息处理,确保 onTouchEvent 不在主线程执行耗时 I/O 或复杂布局。dumpsys input --windows 显示队列占用 < 20%
死锁判断为真在 WMS 锁 mWindowMap 与 mStackId 之间引入锁排序,或使用 tryLock 避免相互等待。多次复现场景下不再出现线程栈死锁

3.5 复测

修复后必须执行以下三步复测流程,确保队列状态恢复正常。每一步都应采集 dumpsys window 数据做前后对比。

  1. 基础功能测试:快速打开和关闭 10 个不同应用,观察窗口动画是否平滑,无掉帧或冻结。
  2. 压力测试:使用 adb shell input swipe 模拟高速连续滑动,同时触发多窗口切换,运行 2 分钟后采集队列深度。
  3. 死锁稳定性测试:在 dumpsys -t 5 window 连续 5 次执行中,保证命令均在超时前返回,且输出中不出现 Timeout waiting for reply。

3.6 预防

长期预防措施:在 CI 流程中集成 dumpsys window | grep "Looper" 基线检查,设定队列深度上限为 300。若超出则自动阻断提交。同时为所有 WMS Handler 的 Message 添加 setMessageName(),确保 dumpsys window 输出的队列日志可追溯耗时消息来源。

此外,建议在系统服务侧开启 Looper.setMessageLogging() 日志,将执行时间超过 50ms 的消息记录到 EventLog 中,便于在生产环境中提前发现队列繁忙趋势。