4. 系统如何生成ANR?Trace文件的生成归纳总结
4.1 ANR触发的三种典型场景
我调过不少ANR,说白了,系统判定ANR就三个入口:
- InputDispatching超时 —— 用户触摸或按键后5秒内没响应,最常见。
- Broadcast超时 —— 前台广播10秒,后台60秒没执行完。
- Service超时 —— 前台服务20秒,后台200秒没完成启动/绑定。
源码里对应三个超时监控类:InputManagerService、BroadcastQueue、ActiveServices。它们都会向AnrHelper发起ANR请求。
4.2 系统诊断ANR的核心流程
一旦超时,AnrHelper会同步收集现场证据。我把关键步骤拆成一张图:
这张图帮你把全链路串起来了。实际代码里,AnrHelper会调用ProcessErrorStateInfo的dumpAnrStateLocked,再触发SignalCatcher让所有线程打印堆栈。
4.3 Trace 文件到底在哪里?长什么样?
我记得刚入行时,以为trace文件在app目录下。后来才发现是系统全局的:
| 设备状态 | 路径 | 关键内容 |
|---|---|---|
| 非root且非eng版 | /data/anr/traces.txt | 所有进程(含system_server)的堆栈 |
| root或eng版 | 同路径 + 额外详细dump | 主线程 + binder线程 + 锁信息 |
| Android 10+ | /data/anr/traces_进程名_时间戳.txt | 每个ANR进程独立文件,更清晰 |
4.4 抓取堆栈的“幕后黑手”:SignalCatcher
系统生成trace时,会向目标进程发送SIGQUIT信号(信号3)。这个信号由SignalCatcher线程接收。什么是SignalCatcher?其实就是Android Runtime里的一个专用线程,它的工作就一件:
- 收到SIGQUIT → 遍历所有Java & Native线程
- 对每个线程执行
Thread::DumpStack() - 把堆栈文本写入
/data/anr/下的文件
你想想看,如果app进程本身已经卡死到连SignalCatcher都跑不动,那trace文件可能只有部分线程信息,甚至为空。我在项目里遇过一次——app在做大量JNI调用导致全局锁竞争,SignalCatcher自己也拿不到锁,结果trace文件里主线程那栏是 --- waiting for lock ---,全靠binder线程信息推断问题。
4.5 我平时分析Trace文件的三个重点步骤
- 先看主线程状态:
Runnable?Blocked?Waiting? 如果是Blocked,后面一定有- waiting to lock <0x...>。 - 看持有锁的线程:找到
- held by thread xxx,看看那个线程在干嘛。 - 看Binder线程:如果主线程在等Binder响应,那binder线程的
outgoing transaction可以暴露远程服务卡在哪。
SharedPreferences.save(),但持有锁的是另一个等待文件IO的线程——真正的根因是文件存储被其他进程占用了。只看主线程会误判。4.6 从系统生成到开发者见到的最终Trace
整个流程可以归纳为一张简短步骤表:
| 步骤 | 执行者 | 操作 |
|---|---|---|
| 1 | AMS / InputManager | 检测到超时,触发ANR |
| 2 | AnrHelper | 收集CPU/Load/线程堆栈等数据 |
| 3 | 系统进程 (system_server) | 发送SIGQUIT给目标进程 |
| 4 | 目标进程 SignalCatcher | 抓取所有线程堆栈,写入文件 |
| 5 | AMS | 弹出ANR对话框,输出logcat |
还有一步容易被忽略:系统会等待signal catcher完成最多30秒。如果目标进程卡死严重,系统在30秒后无论是否写完都会弹出对话框,但trace文件可能不完整。我建议这时候去抓bugreport,里面包含了更多系统级dump。
4.7 个人经验总结
说实话,弄懂系统怎么生成ANR和trace文件,是解决ANR问题的基本功。你不能只盯着java堆栈看,有时候native层、kernel层的卡顿才是真凶。嗯,下次遇到非主线程的ANR(比如service超时),别忘了检查CPU负载和进程优先级。
最后给你一个实用小技巧:如果你想手动触发trace看看效果(特别是调试时),可以执行:
adb shell kill -3 <pid>
adb pull /data/anr/traces.txt
自己在开发板或模拟器上试试,看看生成的文件长什么样。熟悉它,你分析线上ANR会快很多。