第3章 ANR日志采集方法
现象识别:ANR触发条件
当主线程执行Room数据库操作(如查询、插入、事务)超过5秒时,系统会弹出“应用无响应”对话框,并生成ANR日志。核心现象包括:
- 界面卡死超过5秒,用户无法操作
- 系统提示“xxx应用未响应,是否关闭?”
- Logcat中出现
ANR in关键日志行
数据采集:三种核心方法
| 方法 | 命令/工具 | 产出文件 | 关键信息 |
|---|---|---|---|
| 1. 拉取ANR trace文件 | adb pull /data/anr/ | traces.txt 或 anr_*.txt | 线程堆栈、锁等待关系、CPU使用率 |
| 2. Logcat过滤ANR事件 | adb logcat -b events -b main | grep "ANR in" | 实时日志流 | ANR发生时间、进程名、进程PID、原因描述 |
| 3. 提取CPU负载与等待锁 | 分析trace文件中的 held by / waiting to lock 字段 | 筛选后的文本 | 主线程阻塞的具体锁对象、持有者线程、锁类型 |
方法详解
3.1 使用 adb pull /data/anr/ 获取trace文件
在ANR发生后,立即连接设备并执行以下命令:
adb shell ls /data/anr/
adb pull /data/anr/ ./anr_logs
注意:需要设备已root或应用为debuggable版本。若权限不足,可尝试 adb root 后再执行。产出的trace文件中包含所有线程的堆栈,是定位主线程阻塞的根本依据。
3.2 Logcat过滤“ANR in”
使用以下命令捕获系统ANR通告:
adb logcat -b events -b main -b system | grep -E "(ANR in|am_anr)"
输出示例:
01-23 14:32:10.456 789 789 I am_anr : [0,12345,com.example.app,13579,Input dispatching timed out (Window...)]
从中可提取PID、进程名、ANR子类型(输入dispatch超时 / 广播接收器超时 / 服务超时)。
3.3 提取CPU负载与等待锁
在trace文件中搜索锁相关关键字:
grep -E "(held by|waiting to lock|Blocked)" traces.txt
grep -A 5 "main" traces.txt | grep -E "cpu|load|lock"
重点关注主线程堆栈中 waiting to lock <0x...> 及该锁的 held by thread id,结合CPU负载(CPU usage 字段)判断是否因高负载导致锁长时间无法释放。
ANR日志采集流程
以下SVG展示了从ANR发生到综合诊断的完整采集路径,帮助排查人员按步骤操作。
排查表:关键信息提取指引
| 信息来源 | 关键字段/正则 | 排查要点 |
|---|---|---|
| trace文件头 | ----- pid xxx at yyy ----- | 确认ANR发生的确切时间、进程PID |
| 主线程堆栈 | "main" prio=5 tid=1 Blocked | 主线程状态为Blocked(等待锁) |
| 锁等待 | waiting to lock <0x...> | 指向需要获取的对象锁地址 |
| 锁持有者 | held by thread xxx | 找到持有锁的线程ID |
| CPU负载 | CPU usage: X% total, Y% user, Z% kernel | 若总负载 >80% 或 user 负载 >50%,可能因系统繁忙导致锁持有时间延长 |
| Logcat ANR原因 | ANR in com.example.app (pid 12345)Reason: Input dispatching timed out | 区分输入超时(UI线程阻塞)还是其他类型 |
经验规则:若trace中主线程状态为
RUNNABLE 但CPU占用极低(<1%),说明主线程在等待I/O或锁但未调度;若主线程为 BLOCKED 且锁持有者为 Binder:xxx 或 Pool-xxx,则很可能是Room的子线程持锁未释放。
初步定位:从采集数据中快速锚定问题
- 在trace文件中定位主线程(线程名
main),查看其堆栈最后部的waiting to lock行。 - 向上查找该锁地址所在的
held by thread,确认持有锁的线程ID和名称。 - 检查持有锁线程的堆栈,看它是否长时间停留在Room的写操作(如
RoomDatabase的beginTransaction/executeInsert方法)。 - 结合Logcat中的
ANR in时间戳,确认该时间段内CPU负载是否过高(通常高负载会放大锁竞争)。
注意:adb pull /data/anr/ 需要设备具有读写权限。若无法获取trace文件,可尝试等待ANR对话框出现后抓取logcat中的
ANR in 和 am_anr 事件,或启用 adb shell setprop debug.am_anr_dump 1 强制生成更多dump信息。