第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发生到综合诊断的完整采集路径,帮助排查人员按步骤操作。
ANR日志采集流程图 ANR 发生(界面卡顿5秒) 1. adb pull /data/anr/ 获取traces文件 2. Logcat过滤 “ANR in”事件 3. 提取CPU负载 与等待锁信息 生成综合诊断报告

排查表:关键信息提取指引

信息来源关键字段/正则排查要点
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的子线程持锁未释放。

初步定位:从采集数据中快速锚定问题

  1. 在trace文件中定位主线程(线程名 main),查看其堆栈最后部的 waiting to lock 行。
  2. 向上查找该锁地址所在的 held by thread,确认持有锁的线程ID和名称。
  3. 检查持有锁线程的堆栈,看它是否长时间停留在Room的写操作(如 RoomDatabase 的 beginTransaction / executeInsert 方法)。
  4. 结合Logcat中的 ANR in 时间戳,确认该时间段内CPU负载是否过高(通常高负载会放大锁竞争)。
注意:adb pull /data/anr/ 需要设备具有读写权限。若无法获取trace文件,可尝试等待ANR对话框出现后抓取logcat中的 ANR in 和 am_anr 事件,或启用 adb shell setprop debug.am_anr_dump 1 强制生成更多dump信息。