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 调用或跨进程通信积压 |
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