ANR 定义与触发场景:主线程阻塞、无响应的阈值、系统日志特征
1.1 什么是 ANR
Application Not Responding(ANR)是 Android 系统对应用主线程(UI 线程)长时间未完成事件处理或未响应输入事件时,主动触发的保护机制。当用户操作(如点击、触摸、按键)无法在规定时间内被主线程处理完毕,系统会弹出一个“应用无响应”对话框,用户可选择“等待”或“关闭应用”。ANR 的本质是主线程阻塞——任何耗时操作(网络请求、磁盘 I/O、复杂计算、锁竞争等)直接放在主线程执行,都可能导致 ANR。
核心原则:Android 主线程负责所有 UI 绘制、事件分发、广播接收、Service 生命周期回调,任何阻塞超过阈值都会触发 ANR。
1.2 触发场景与阈值
不同组件有不同的阈值时间,系统从收到事件/请求开始计时,若主线程在指定时间内未完成处理,则判定 ANR。常见触发场景及对应阈值如下表:
| 组件 / 事件类型 | 阈值(超时时间) | 触发条件描述 |
|---|---|---|
| Activity 输入事件 | 5 秒 | 系统发送了触摸/按键事件,主线程 5s 内未完成 dispatch 和 onUserInteraction |
| Service 启动 / 绑定 | 20 秒 | Service 的 onCreate / onStartCommand / onBind 未在 20s 内返回 |
| ContentProvider 超时 | 10 秒 | ContentProvider 的 onCreate 或 query / insert / update / delete 长时间未返回 |
| BroadcastReceiver onReceive(同步) | 10 秒 | 主线程 BroadcastReceiver 的 onReceive 执行超过 10s(前台广播);后台广播阈值更短(约 3-5s) |
| Input 事件堆积 | 5 秒 | 输入队列中某个事件超过 5s 未被处理(即使前一个事件未完成) |
💡 常见误区:ANR 并不仅发生在用户可见的 Activity 中。后台 Service、ContentProvider、BroadcastReceiver 同样会触发 ANR,且阈值更短。
1.3 系统日志特征
当 ANR 发生时,系统会将详细的堆栈信息写入 /data/anr/traces.txt(Android 10 之后会分文件存储),同时通过 logcat 输出关键日志。识别 ANR 的关键日志特征包括:
- Logcat 中出现的标记:
ANR in加上应用包名,例如:
I/ActivityManager( 1234): ANR in com.example.myapp (com.example.myapp/.ui.MainActivity)
I/ActivityManager( 1234): Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 5000.0ms ago.)
I/ActivityManager( 1234): Load: 2.34 / 1.98 / 1.67
I/ActivityManager( 1234): CPU usage from 0ms to 6193ms later (2023-08-15 10:30:00.123):
I/ActivityManager( 1234): 7% 1234/system_server: 2% user + 1% kernel / faults: 123 minor
I/ActivityManager( 1234): 92% 5678/com.example.myapp: 80% user + 12% kernel / faults: 456 minor 3 major
- traces.txt 文件内容:主线程以及所有存活线程的堆栈信息,主线程通常堆栈卡在某个锁、sleep、I/O 函数上。示例:
----- pid 5678 at 2023-08-15 10:30:06.123 -----
Cmd line: com.example.myapp
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 dsCount=0 flags=1 obj=0x72b00000 self=0x7c800000
| sysTid=5678 nice=0 cgrp=default sched=0/0 handle=0x7c800000
| state=S schedstat=( 123456789 1000000 2345 ) utm=123 stm=45 core=0 HZ=100
| stack=0x7c600000-0x7c620000 stackSize=8192KB
| held mutexes=
at java.lang.Thread.sleep(Native Method)
at com.example.myapp.MainActivity.onClick(MainActivity.java:45)
... (frame省略)
⚠️ 关键排查点:traces.txt 中主线程的
state 字段常见值为 Blocked(等待锁)、Sleeping(主动睡眠)、Runnable(长时间 CPU 计算)。如果主线程持续 Runnable 状态且长时间未 submit 新事件,通常是由于死循环或巨大计算量导致。
1.4 ANR 触发流程 SVG
1.5 实际排查调试方法
当你需要确认是否发生 ANR 时,按以下步骤确认:
- 实时查看 logcat:过滤
ANR in或ActivityManager级别为 I/W。 - 获取 traces.txt:通过 adb 拉取
/data/anr/traces.txt(需要 root 或 debug 版)。 - 查看主线程堆栈:在 traces 中查找以
"main" prio=5 tid=1开头的线程块,定位卡住的具体方法。 - 分析嵌套锁:如果主线程状态为
Blocked,查看它正在等待哪个锁,并寻找持锁线程。 - 检查 CPU 负载:结合 metrics 看是否系统资源不够,CPU 使用率过高会导致主线程调度延迟。