一、什么是 ANR?先聊聊我的理解

我先说说我自己的感受吧。刚入行那会儿,我总以为 ANR 是什么高深的系统故障,后来排过几百个日志才发现——说白了,它就是主线程被堵住了,用户没法操作了。

ANR 的全称是 Application Not Responding,也就是应用无响应。Android 系统会一直盯着你的主线程,一旦发现超过某个时间阈值还没法处理用户输入或者完成任务,它就会弹出一个对话框:“××应用无响应,是否继续等待或者关闭?”这个弹窗对用户来说非常烦躁,对开发者来说更是噩梦。

1.1 ANR 的定义

官方定义:当应用的主线程被长时间阻塞,导致无法响应用户操作或系统关键事件时,系统就会判定为 ANR。我个人习惯把它理解成「主线程的交通拥堵」——车流太多,或者路上出了事故,后面的车就全堵死了。

1.2 触发条件

ANR 不是随便就能触发的,它有一组明确的超时阈值。我用一个表格把常见的几种类型列出来,你一看就明白了。

ANR 类型 触发场景 超时时间
输入事件 ANR 屏幕触控、按键等事件派发超时 5 秒
广播 ANR BroadcastReceiver 的 onReceive 未在限定时间内返回 前台 10 秒 / 后台 60 秒
服务 ANR Service 的 onCreate、onStartCommand 等执行超时 20 秒
ContentProvider ANR Provider 查询或更新耗时过长 20 秒

嗯,这里要注意一下——前台广播和后台广播超时时间不一样,后台给的时间更宽裕,但也更容易出现 ANR,因为现在的系统对后台限制越来越严。

避坑提示: 我曾经排查过一个诡异的 ANR,日志显示 BroadcastReceiver 耗时只有 8 秒,按理说没超过前台 10 秒的阈值,为什么还弹 ANR?后来发现是 onReceive 里面调了 goAsync(),但是 finish() 没及时调用。所以你记住:用了异步就要负责到底。

1.3 对用户体验的影响

用户遇到 ANR 弹窗的时候,通常只有两个选择——“等待”或“关闭”。你想想看,如果用户点了“关闭”,那你的应用直接就没了。更糟糕的是,如果用户连续多次遇到 ANR,他大概率会卸载。我在做电商 App 时统计过,ANR 率超过 1% 的应用,次日留存率会下降 15% 以上。

其实 ANR 本身不可怕,可怕的是你不知道它为什么发生。接下来我会带你从日志一步步拆解,找到根因。

ANR 触发流程与影响 主线程阻塞 输入事件超时 (5s) 广播/服务/Provider超时 系统主动检测 ANR 弹窗出现 用户点击「等待」 用户点击「关闭」 应用可能恢复,但体验已受损 应用退出,用户可能卸载
一点经验: 我处理过的 ANR 案例中,大约 70% 都是输入事件 ANR,而且大多和主线程做耗时操作有关(比如网络请求放主线程、大文件读写、View 层过度绘制)。剩下的 30% 才是各种 BroadcastReceiver 和 Service 的超时。所以抓到输入 ANR 日志时,先查主线程堆栈,大概率能找到元凶。

1.4 系统如何判定 ANR?

说白了,系统里有一套监控机制:InputDispatcher 负责分发输入事件,ActivityManagerService 负责监控广播和服务。当发现超时后,AMS 会收集当前进程的主线程堆栈、CPU 占用、Binder 调用状态等信息,然后写入 /data/anr/traces.txt(Android 10 之前)或者 /data/anr/anr_* 文件(新版本)。

我个人建议你拿到 ANR 日志后,第一件事就是去翻 CPU 负载那一块。为什么?因为很多时候 ANR 并不是你的代码跑得慢,而是整个系统太忙了,主线程根本分不到 CPU 时间片。这种情况你得看系统全局的负载,而不是只盯着自己的线程。

// 一个典型的 traces.txt 头部片段(以 Android 8.0 为例)
----- pid 12345 at 2025-01-15 10:23:45 -----
Cmd line: com.example.yourapp
CPU usage:
  9.8% total, 2.3% user, 7.5% kernel, 0% iowait
  Load: 12.5 / 10.8 / 9.2
  // 如果 Load 远大于 CPU 核心数,说明系统已经过载
重要: 千万别只盯着自己的 App 代码。我曾经有一次被坑惨了,traces 显示主线程停在 Looper.loop 里,我以为是没有收到消息。结果仔细一看系统 CPU Load 高达 40(8 核机器),原来是别的 App 在疯狂写日志,系统 I/O 堵死了。所以,ANR 根因分析一定要结合「系统级指标」来看。

1.5 ANR 对用户体验的量化影响

我参与过的一个日活百万级的 App 做了个实验:ANR 率从 0.8% 降到 0.2% 后,次日留存提升了 1.3 个百分点。你想想看,这一个百分点放在百万日活上就是一万个用户。所以优化 ANR 不只是修复 bug,更是实实在在的生意。

  • 短期影响: 应用冻结 5~60 秒 + 弹窗,用户被迫等待或退出。
  • 中期影响: 系统可能杀掉进程,导致用户丢失未保存的数据或页面状态。
  • 长期影响: 用户对 App 产生「卡」「慢」「不稳定」的负面印象,卸载率上升。

嗯,说到数据丢失,我记得有一次线上 ANR 日志显示主线程在执行 SQLiteDatabase.insertOrThrow 时卡住,用户正在填写订单,结果 ANR 弹窗后进程被杀死,订单信息全丢了。最终用户投诉到客服,又花了一个星期做补偿方案。所以如果涉及数据库操作,尽量放到子线程并加上事务超时机制。

1.6 本章小结

ANR 并不神秘,它只是一个信号——主线程被长时间占用了。触发条件主要有四类(输入、广播、服务、Provider),每类的超时时间不同。对用户来说,ANR 带来的体验伤害是直接的、不可逆的。后面我们就要开始真正动手了,从 traces 日志里一行一行找出凶手。

记住一句话:ANR 日志是系统送给你的免费调试报告。别浪费它。