1. ANR现象识别与用户感知

1.1 什么是ANR?卡顿与冻结的边界

ANR(Application Not Responding)是Android系统在检测到应用主线程在规定时间内未完成指定操作时,强制向用户展示的系统级弹窗。它与普通卡顿的关键区别在于:卡顿是可恢复的短暂掉帧,而ANR意味着用户操作被彻底挂起,系统将给出“关闭 or 等待”的强制选择。

卡顿边界判断规则:
  • 输入事件(Input Dispatching)超时:主线程处理按键/触摸事件超过 5s(Android 10+ 为 3.5s)→ ANR
  • 广播接收器(BroadcastReceiver)超时:前台广播 10s、后台广播 60s 未执行完 → ANR
  • 服务(Service)超时:前台服务 20s、后台服务 200s → ANR
  • ContentProvider 超时:Provider 启动/查询超过 10s → ANR

1.2 用户感知维度:用户反馈特征

用户对ANR的感知一般经历三个阶段:

  1. 冻结阶段:触摸屏幕无响应、滑动卡死、动画静止。
  2. 弹窗阶段:系统弹出“应用无响应”对话框,用户可选择“等待”或“关闭应用”。
  3. 退出阶段:若用户选择关闭或系统自动杀死进程,应用闪退。
用户反馈特征 典型描述 对应技术指标
点击无反应 点了按钮没动静 InputDispatchingTimingOut(>500ms 已感知)
页面停留卡死 一直转圈圈,不消失 主线程消息队列堆积 >5s
弹窗出现 弹出一个框问我要不要关掉 ANR 弹窗触发(对应各超时类型)
应用自动关闭 闪退到桌面 ANR 后系统杀进程

1.3 系统弹窗识别与日志关键字

系统弹窗是ANR最直接的标志,但其文字因Android版本而异。同时,logcat 中会产生特定的关键字,是排查的重要入口。

日志关键字快速检索表:
关键字 含义 出现位置
ANR in 标记发生ANR的进程 am_anr log
Reason: 超时原因(如 Input dispatching timed out) traces.txt / logcat
Long wait 主线程等待某个锁超长 traces.txt
"main" thread 主线程堆栈的关键标记 traces.txt
CPU usage CPU 负载信息 traces.txt 末尾
Watchdog 系统看门狗检测到死锁或严重问题 event log / kernel log

1.4 故障树:ANR现象识别排查

下图展示了从用户感知到日志确认的排查流程,帮助工程师在接到反馈时快速定位ANR现象。

ANR现象识别故障树 用户反馈“卡死/弹窗” 系统弹窗出现 长时无响应 应用自动退出 查看logcat 搜索“ANR in” 检查操作间隔 >5s 大概率ANR 收集/data/anr/ traces.txt 确认:ANR 事件 进入下一步:数据采集 提示:若用户反馈“闪退”但无弹窗,仍需检查traces.txt,因低内存设备可能直接杀进程而不弹窗。

1.5 日志获取命令与trace示例

当确认ANR后,应第一时间抓取以下数据:

# 获取当前ANR trace文件(需要root或adb 权限)
adb pull /data/anr/traces.txt

# 实时过滤ANR日志
adb logcat -b main -b events -b system | grep -E "ANR|am_anr"

# 查看上次ANR的时间戳(Android 10+)
adb logcat -b events -d | grep "am_anr"

下面是一段典型的 traces.txt 堆栈摘录,展示了主线程因文件读写阻塞导致的ANR:

----- pid 12345 at 2025-03-21 14:30:22 -----
Cmd line: com.example.app
...
"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x7fc8f120
  | sysTid=12345 nice=0 cgrp=default sched=0/0 handle=0x7f8e0c4000
  | state=B schedstat=( 12345678 23456789 123 ) utm=120 stm=80 core=4 HZ=100
  | stack:
  at java.io.FileOutputStream.write(Native Method)
  at java.io.FileOutputStream.write(FileOutputStream.java:326)
  - locked <0x08a7f1a0> (a java.io.FileOutputStream)
  at com.example.app.utils.FileLogger.writeLog(FileLogger.java:45)
  - waiting to lock <0x08a7f200> (a java.io.FileOutputStream)
  at com.example.app.MainActivity.onCreate(MainActivity.java:30)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3420)
  ...
  at android.os.Handler.handleCallback(Handler.java:883)
  at android.os.Handler.dispatchMessage(Handler.java:100)
  at android.os.Looper.loop(Looper.java:214)
  at android.app.ActivityThread.main(ActivityThread.java:7682)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:517)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:961)

CPU usage: 45% total, 12% app, 33% system
Load: 4.5 / 3.8 / 2.6
注意: 上述trace中“waiting to lock”表明主线程在等待文件锁,而锁被其他线程持有。该情形属于同步锁竞争导致的ANR,需要进一步分析持锁线程。

1.6 排查表:快速判断ANR类型

现象 可能原因 优先检查点
点击按钮一直无响应,5秒后弹窗 主线程耗时操作(IO、网络、锁等待) 主线程堆栈,查看是否被CPU/IO阻塞
从后台切回前台时卡死 BroadcastReceiver onReceive 耗时 logcat 搜索 “Broadcast of intent”,trace中找“onReceive”
启动页后白屏,然后弹ANR Service 启动或 ContentProvider 初始化超时 trace中找“handleCreateService”或“publishContentProviders”
系统截图提示“应用无响应”但未弹窗 系统服务看门狗检测,通常伴随 log “Watchdog” dmesg 或 event log 搜索“Watchdog”

1.7 本章小结