1. 无障碍线程故障现象分类

SystemUI 的无障碍线程(Accessibility Thread)承担着响应无障碍服务请求、遍历视图树、填充无障碍事件等关键任务。其异常现象可归纳为五类:ANR、卡顿、死锁、内存泄漏、服务超时。本课首先建立故障树,帮助排查人员将报告症状快速映射到具体故障类别。

核心判断规则: 收到用户反馈后,首先区分是“弹窗提示无响应”(ANR)、“操作反馈延迟”(卡顿)、“状态完全卡死无法交互”(死锁/服务超时)还是“内存持续上涨直至被系统杀死”(内存泄漏)。

1.1 故障现象分类与排查总览表

故障类别 表层现象 典型症状日志关键词 排查切入点
ANR 弹出“系统UI无响应”对话框 ANR in com.android.systemui 主线程与无障碍线程竞争锁
卡顿 屏幕触摸延迟、无障碍焦点切换不顺畅 Choreographer was late 无障碍事件处理耗时 > 16ms
死锁 整个 SystemUI 完全停止响应,无 ANR 弹窗 Found 1 deadlock dump 线程栈查看等待链
内存泄漏 SystemUI 内存持续增长,常伴随被 LMK 杀掉 Out of memory 泄漏的 AccessibilityNodeInfo 对象
服务超时 TalkBack 等第三方服务连接中断、重新绑定 AccessibilityService timeout Binder 调用或跨进程通信积压
无障碍线程故障树——故障现象分类与对应关系 故障现象(用户感知) ① ANR ② 卡顿 ③ 死锁 ④ 内存泄漏 ⑤ 服务超时 原因:主线程锁等待 超 5s 未释放 原因:事件处理密集 视图遍历超出阀值 原因:线程间锁冲突 形成等待环 原因:未释放 node 引用 导致对象堆积 原因:Binder 调用积压 服务端响应慢

1.2 现象识别:如何判断属于哪一类故障

用户或测试人员的原始描述往往是模糊的。下表的判断规则可帮助你快速筛选数据:

描述示例 优先怀疑 需排除的干扰
“点了一个按钮,等了很久才响应” 卡顿 / ANR 低端机缓慢 ≠ ANR
“屏幕卡住了,什么操作都没用” 死锁 / 服务超时 Binder 死循环
“用 TalkBack 一段时间后系统重启” 内存泄漏 / OOM 其他应用内存泄漏

1.3 数据采集指令集

当症状明确后,立刻执行以下命令采集快照,避免现场丢失:

建议: 将以下命令写入自动化脚本。人工复制粘贴会丢失首次ANR发生时的实时数据。
# 1. 获取 SystemUI 进程 PID
adb shell pidof com.android.systemui

# 2. 发生 ANR 时获取 traces.txt(制造早期现场)
adb shell ls /data/anr/
adb pull /data/anr/traces.txt

# 3. 采集 SystemUI 主线程与无障碍线程栈(非 ANR 时现场)
adb shell dumpsys activity processes | grep "com.android.systemui"

# 4. 死锁初步检查(不需要 root,但需 system 权限)
adb shell dumpsys meminfo com.android.systemui | grep "PSS"

# 5. 无障碍服务绑定状态快照
adb shell dumpsys accessibility

# 6. 开启事件追踪
adb shell setprop debug.atrace.app com.android.systemui
adb shell atrace f req ss sched freq idle -b 2048 -c -t 20 > /tmp/trace_output

1.4 日志关键字排查表

以下是每种故障在 logcat 中的标志性关键字,匹配后可直接跳转到对应的原因定位章节:

故障类 logcat 匹配模式 动作
ANR ANR in com.android.systemui + Waiting in main thread 查看主线程堆栈,寻找 LockedObject
卡顿 Choreographer: Skipped 47 frames! + AccessibilityInteractionClient 抓取 Systrace 分析无障碍事件洪水
死锁 Found 1 deadlock 或 Thread-5 waiting to lock <0x...> 在 traces.txt 中找到 waiting for 环
内存泄漏 GC trying to free large object + AccessibilityNodeInfo queue size increasing 用 MAT 分析堆转储(hprof)
服务超时 AccessibilityService timeout [Package] 或 onAccessibilityEvent() took too long 查看该服务的调用链 Trace

1.5 典型日志片段与眼力训练

// 场景:ANR 现场截获
02-25 14:23:45.678  1234  5678 E ActivityManager: ANR in com.android.systemui
02-25 14:23:45.679  1234  5678 E ActivityManager: PID: 8910
02-25 14:23:45.680  1234  5678 E ActivityManager: Reason: Input dispatching timed out (Window: aa...)
02-25 14:23:45.681  1234  5678 E ActivityManager: Load: 12.9 / 8.1 / 4.3
// 无障碍线程卡在锁获取:
02-25 14:23:45.700  8910  9100 D Thread-5: "AccessibilityThread" prio=5 tid=19 Blocked
02-25 14:23:45.701  8910  9100 D Thread-5:   at com.android.systemui.statusbar.policy.AccessibilityController$MyHandler.handleMessage - waiting to lock <0x03c6b7c8>
训练要点: 看到 Waiting to lock 说明是死锁或锁竞争;看到 Input dispatching timed out 但无障碍线程不在栈里,可能问题出在主线程而非无障碍线程。

1.6 修复动作与预防方案(入门版)

修复动作(简明):

  • ANR/卡顿: 将耗时操作从 AccessibilityThread 迁移到异步 BgThread;避免在锁块内调用无障碍视图方法。
  • 死锁: 锁顺序固定化,使用 ReentrantLock.tryLock(timeout) 替代 synchronized。
  • 内存泄漏: 在 clearAccessibilityFocus() 时断开 node 引用,设置弱引用监听。
  • 服务超时: 为 Binder 调用设置调用超时;增大 onAccessibilityEvent 的队列容量或实现限流。

预防方案(开发流程):

  • 在 CI/Monkey 测试中,集成 dumpsys accessibility 并检查 event 队列深度。
  • 对 AccessibilityThread 添加 StrictMode 策略,捕获违规磁盘 I/O 或长锁。
  • 每周自动化查看 SystemUI 堆栈,使用 hprof 静态分析查找常见泄漏模式。

1.7 复测验证检查点

# 在修复后执行的最简回归检查(3 条)
1. adb shell dumpsys accessibility  # 检查 no service timeout
2. 操作 TalkBack 焦点切换 10 次     # 目测无跳帧 / 延迟 > 150ms
3. 遍历所有通知面板                # 确保无未释放 node 导致 OOM