ANR日志获取与基本分析方法
分析ANR,第一步就是拿到日志。说起来简单,但真到实战的时候,不少人会卡在这一步——要么不知道怎么导出,要么导出的文件不对。我个人习惯先确认日志是否完整,再开始分析。本章我就带你走一遍日志获取的完整路径,还有最基本的分析方法。
一、ANR日志的三种获取方式
不同场景下,获取日志的方式不太一样。我一般按优先级来:能用adb就用adb,手机在身边就直接导出,线上问题就靠bugreport。下面这三种方式,基本覆盖了所有情况。
适用场景:手机在手,且已root或具有system权限。
# ANR日志默认路径
/data/anr/traces.txt
# 使用文件管理器或Android Studio的Device File Explorer导出
需要注意的是,Android 11+ 对 /data/anr 目录做了权限限制,非root设备可能无法直接读取。这时可以用下面两种方式替代。
适用场景:开发调试阶段,电脑连接手机。
# 拉取traces文件
adb pull /data/anr/traces.txt .
# 或者直接拉取整个anr目录
adb pull /data/anr/ .
# 生成完整bugreport(包含所有日志)
adb bugreport bugreport.zip
我个人习惯直接用 adb bugreport,因为它会把traces.txt、events.log、system.log打包在一起,省得一个个找。
适用场景:线上问题、用户反馈、或者设备无法直接adb连接。
# 在设备上生成bugreport(无需root)
adb bugreport bugreport.zip
# 解压后,ANR相关日志在
bugreport/FS/data/anr/traces.txt
二、三大核心日志文件
拿到日志压缩包,里面文件很多。但你不用全看,盯住下面这三个就行。它们各自记录了不同维度的信息,配合起来用效果最好。
| 文件名 | 核心内容 | 分析价值 |
|---|---|---|
| traces.txt | 所有线程的堆栈信息,主线程状态、锁等待关系 | ⭐ 最高,直接定位卡顿点 |
| events.log | 系统事件的时间线,ANR触发前后的关键动作 | ⭐ 确认ANR发生时机和上下文 |
| system.log | 系统级别的运行日志,包括CPU、内存、IO信息 | 辅助判断系统负载是否过高 |
三、ANR基本分析四步法
拿到traces.txt之后,怎么下手?我总结了一套四步法,用熟了基本能覆盖90%的ANR场景。
- 第一步:定位时间点 —— 在traces.txt里搜索 "ANR in",找到ANR发生的具体时间和进程。
- 第二步:找到主线程 —— 搜索 "main" 或 "tid=1",看主线程当前状态和堆栈。
- 第三步:分析锁状态 —— 看主线程是否在等待某个锁,谁持有了这个锁。
- 第四步:追溯调用链 —— 从主线程堆栈的最底层往上读,找到业务代码的入口。
举个例子,下面这段traces片段,主线程卡在了 Waiting 状态:
----- pid 12345 at 2025-01-15 10:30:00 -----
Cmd line: com.example.app
"main" tid=1 Waiting
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:423)
at com.example.data.local.DatabaseHelper.query(DatabaseHelper.java:85)
- waiting on <0x0c4d5e60> (a com.example.data.local.DatabaseLock)
at com.example.ui.MainActivity.loadData(MainActivity.java:42)
...
看到了吗?主线程在 DatabaseHelper.query 里等一个锁。这时候我们去搜 0x0c4d5e60,就能找到谁拿着这个锁不放。我曾经遇到过一个案例,子线程在写数据库时没释放锁,导致主线程ANR。嗯,这种锁竞争问题,在traces里其实非常明显。
grep -n "main" traces.txt 定位主线程行号,然后看上下10行,基本就能判断状态。如果看到 "BLOCKED" 或 "Waiting",九成九是锁竞争。
四、实战经验与避坑指南
分析ANR多了,你会发现有些坑是共性的。我说几个常见的,你碰到的时候可以少走弯路。
- 误区1:只看traces.txt,不看events.log —— 有时候ANR是系统负载高导致的,traces里看不出明显问题,但events.log里会记录CPU使用率飙升。我建议两个文件对照着看。
- 误区2:忽略子线程的锁持有 —— 主线程等锁,锁在子线程手里,但子线程也在等别的资源。这种"锁链"在traces里会连续出现,需要顺着 tid 一个个查。
- 误区3:日志不全就放弃分析 —— 万一traces.txt被覆盖了,还可以从bugreport的
dumpstate_board.txt里找到部分信息。别轻易放弃。
说到底,ANR分析就是一个"找线索"的过程。日志就是你的现场证据,拿到完整的日志,顺着主线程堆栈一路往下摸,总能找到真相。多练几次,你也会形成自己的分析直觉。