ANR基础概念:从定义到系统机制
我刚开始做 Android 开发时,第一次遇到 ANR,那会儿完全不知道发生了什么。屏幕弹出对话框「应用无响应」,用户只能选择等待或关闭。后来我才搞明白——ANR 就是主线程被卡住了,系统觉得你不行了。
ANR 全称 Application Not Responding。说白了,就是主线程在规定时间内没有处理完该做的事。系统为了保证用户体验,会强行弹窗告诉你:这个应用可能没救了。
每种 ANR 的触发场景和超时时间都不一样。我整理了一张表,方便大家对照排查。
| ANR 类型 | 超时时间 | 典型触发点 | 堆栈特征 |
|---|---|---|---|
| 输入事件 | 5秒 | 按键、触摸 | 主线程在 InputEvent 或 handleMessage |
| 广播接收器(前台) | 10秒 | onReceive() | 主线程执行耗时操作 |
| 广播接收器(后台) | 60秒 | onReceive() | 同上,超时更长 |
| Service(前台) | 20秒 | onCreate / onStartCommand | 主线程阻塞 |
| Service(后台) | 200秒 | onCreate / onStartCommand | 同上 |
| ContentProvider | 无固定值 | query / insert 等 | 主线程执行数据库或网络请求 |
系统是怎么检测的?
内部有一个 watchdog 线程专门盯着主线程。当输入事件被分发后,系统会启动一个 5 秒的定时器。如果 5 秒内主线程的 Looper 还没处理这条消息,定时器就会炸。然后系统马上收集主线程堆栈,写到 /data/anr/traces.txt,同时弹出对话框。
广播和 Service 的机制差不多,只是超时时间不同。这里注意一点:前台广播的超时只有 10 秒,后台广播是 60 秒。我曾经排查过一个案例,开发者把广播注册在清单里,属于后台广播,但他在 onReceive 里做了网络请求,结果用户反馈在低端机上经常 ANR——就是那 60 秒的宽限也被耗尽了。
核心要点:ANR 的本质是主线程的 Looper 在指定时间内没有处理完消息。系统通过计时器 + 堆栈快照来判定和记录。
常见原因有哪些?
- 主线程执行 IO 操作(网络、文件、数据库)
- 复杂布局渲染或大量 Bitmap 解码
- 线程死锁或锁竞争
- Binder 调用长期不返回
- 大量 Log 输出造成磁盘 IO 压力
- 长时间 GC 暂停(尤其在低端机上)
我遇到过最奇葩的一次是,某同事在 Activity 的 onCreate 里直接调用了 Thread.sleep(15000) 来做所谓的「欢迎页停留」——结果用户还没看到欢迎页,ANR 先弹出来了。
避坑指南:千万不要在主线程中调用 Thread.sleep()、发起同步网络请求或执行大文件读写。我曾经见过一个案例,开发者在点击事件里直接用 OkHttp 的同步请求,导致 ANR 频发,最后只能加上请求超时回调才解决。
一个典型的 ANR 现场
看看这段代码,为什么它会触发输入事件 ANR?
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
// 模拟耗时操作:睡眠10秒
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
答案很明显:点击事件是在主线程执行的。睡眠 10 秒 > 5 秒的超时阈值,系统就会判定 ANR。实际项目中你不会直接写 sleep,但数据库查询、图片解码、文件 IO 都可能造成类似的效果。
小技巧:当你拿到一份 ANR 的 traces.txt,先看堆栈中最顶层的方法是不是在主线程("main" 线程)。如果是,再看它卡在哪一步——FileInputStream.read、BitmapFactory.nativeDecodeByteArray、SQLiteSession.executeForCursorWindow 这些都是高频嫌疑人。
往下我们来看这个章节的知识体系。下面这张图帮你把 ANR 的基本骨架理清楚。
理解这些基础概念后,你就能读懂 ANR 日志里的关键字了。举个例子,当你在 traces 里看到 InputDispatcher 相关字样,那基本就是输入事件超时。看到 BroadcastQueue 就是广播 ANR。每种类型对应的排查方向都不一样。
嗯,这一章我们主要把 ANR 的「家底」摸清了。后面章节会深入实战,用具体案例教你如何从日志、堆栈、代码层面一步步定位根因。