4. 系统如何生成ANR?Trace文件的生成归纳总结

4.1 ANR触发的三种典型场景

我调过不少ANR,说白了,系统判定ANR就三个入口:

  • InputDispatching超时 —— 用户触摸或按键后5秒内没响应,最常见。
  • Broadcast超时 —— 前台广播10秒,后台60秒没执行完。
  • Service超时 —— 前台服务20秒,后台200秒没完成启动/绑定。

源码里对应三个超时监控类:InputManagerService、BroadcastQueue、ActiveServices。它们都会向AnrHelper发起ANR请求。

避坑指南:我曾经踩过一个坑——后台广播里的网络请求没设超时,结果服务端挂了,广播线程一直等socket数据,60秒后直接炸ANR。嗯,从那之后我所有耗时操作都强制加Timeout。

4.2 系统诊断ANR的核心流程

一旦超时,AnrHelper会同步收集现场证据。我把关键步骤拆成一张图:

ANR 生成与 Trace 文件核心流程 InputDispatching 超时 Broadcast 超时 Service 超时 AnrHelper.appNotResponding() 数据收集阶段(同步进行) CPU / 内存 / IO 采样 各线程堆栈抓取 锁竞争信息 写入 /data/anr/traces.txt 生成后系统弹出ANR对话框,同时输出 logcat 关键日志

这张图帮你把全链路串起来了。实际代码里,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进程独立文件,更清晰
重点:Trace文件里面最核心的是主线程(main)的堆栈,以及它当前到底在等什么锁。我调了上百个ANR,90%都能通过主线程堆栈直接定位到“卡死在哪个函数”。

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文件的三个重点步骤

  1. 先看主线程状态:Runnable? Blocked? Waiting? 如果是Blocked,后面一定有- waiting to lock <0x...>。
  2. 看持有锁的线程:找到- held by thread xxx,看看那个线程在干嘛。
  3. 看Binder线程:如果主线程在等Binder响应,那binder线程的outgoing transaction可以暴露远程服务卡在哪。
警告:不要只看主线程!我经历过一个诡异ANR:主线程在等SharedPreferences.save(),但持有锁的是另一个等待文件IO的线程——真正的根因是文件存储被其他进程占用了。只看主线程会误判。

4.6 从系统生成到开发者见到的最终Trace

整个流程可以归纳为一张简短步骤表:

步骤执行者操作
1AMS / InputManager检测到超时,触发ANR
2AnrHelper收集CPU/Load/线程堆栈等数据
3系统进程 (system_server)发送SIGQUIT给目标进程
4目标进程 SignalCatcher抓取所有线程堆栈,写入文件
5AMS弹出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会快很多。