1. 什么是ANR?ANR的定义与表现

ANR 这三个字母,做 Android 开发的没有不认识的。全称是 Application Not Responding——应用无响应。说白了,就是你的 App 主线程卡住了,系统等得不耐烦了,直接弹个对话框问用户:你要不要关掉这个 App?

我在项目中第一次遇到 ANR 时,还以为是个小 bug。结果查了一下午,才发现是网络请求没做超时控制,主线程直接挂起等响应。嗯,从那以后我再也不敢小看 ANR 了。

ANR 的定义

官方定义很简洁:当应用的 UI 线程(主线程)无法及时处理用户输入或系统广播时,Android 系统就会判定该应用发生 ANR。系统会弹出一个系统级对话框,用户可以选择「等待」或「关闭应用」。

核心要点:ANR 的本质是主线程阻塞,系统给了你一个「超时红线」。

你想想看,主线程负责什么?处理触摸事件、绘制界面、响应广播、启动服务、解析 ContentProvider……一旦主线程去干耗时任务,比如直接请求网络、读写大文件、执行复杂计算,系统一检测到超时就炸了。

ANR 的表现形式

ANR 出现后,用户能看到明显的对话框,但背后还有更重要的日志和文件。我习惯把 ANR 的「三件套」列出来:

  1. 系统弹窗:白色背景、标题为「应用无响应」或「App isn't responding」,下面有「关闭应用」和「等待」两个按钮。
  2. Logcat 日志:会输出类似 ANR in com.example.app (com.example.app/.MainActivity) 的条目,并包含 CPU 使用率、线程堆栈等信息。
  3. trace 文件:系统在 /data/anr/ 目录下生成 traces.txt(或 traces_xx),里面记录了各线程的堆栈信息。这是定位 ANR 根因的关键线索。

我个人遇到 ANR 时,第一件事就是去拿 trace 文件。用 ADB 命令 adb pull /data/anr/traces.txt ./ 就能拉下来。别只看弹窗,弹窗只告诉你出事了,不告诉你为什么出事。

不同组件的超时阈值

ANR 的判定不是一刀切的,不同场景的「超时红线」不一样。我把最常见的阈值整理成表格,方便你对照:

组件类型 超时时间 说明
输入事件(InputDispatch) 5 秒 用户触摸、按键等操作,主线程 5 秒内没有处理完毕
前台广播(Foreground Broadcast) 10 秒 发送到前台进程的广播接收器,onReceive 方法超时
后台广播(Background Broadcast) 60 秒 后台进程的广播接收器,超时时间较长,但仍会发生 ANR
Service 20 秒 onStartCommand 或 onCreate 等生命周期方法执行超时
ContentProvider 10 秒(早期版本),部分设备不同 ContentProvider 的 query/insert/update/delete 超时

这里有个坑:很多人以为后台广播 60 秒就不会 ANR。我曾经在一个项目里看到开发者在广播里做了大批量数据库操作,虽然没到 60 秒,但 30 多秒的耗时还是让用户感觉卡顿,系统也会在 log 里记录「慢广播」警告。嗯,虽然没弹 ANR 对话框,但已经是潜在风险了。

ANR 的常见触发场景

根据我多年的踩坑经验,ANR 的罪魁祸首主要集中在以下几类:

  • 网络请求:主线程直接使用 HttpURLConnection 或 Retrofit 的同步请求,网络一慢就卡死。这也是我们这门课的核心关联点——Retrofit 配合 RxJava 时,如果没切线程,很容易引发 ANR。
  • 磁盘 I/O:在主线程读写大文件、做 SharedPreferences 大量写入或 SQLite 查询。SharedPreferences 的 commit() 是同步的,写到 ANR 的例子我见过好几次。
  • 复杂计算:比如图片缩放、JSON 解析、序列化等。尤其是 Gson 解析大 JSON 字符串时,几千行的列表解析就可能超过 5 秒。
  • 锁竞争与死锁:主线程在等一个锁,而持有锁的线程又在等另一个资源,形成死循环。
  • 系统资源耗尽:CPU 被其他进程跑满,或者线程池无限创建导致内存紧张,主线程即使不干活也分不到时间片。

避坑指南:我曾经遇到过这样一个案例——App 中的一个广播接收器在 onReceive 里直接调用了 Retrofit 的同步请求,而服务端偶尔响应慢,前台广播 10 秒超时直接触发 ANR。后来改成用异步请求 + 回调,问题解决。记住:主线程永远不要碰网络,这是铁律。

ANR 的触发流程(图解)

下面我用一张流程图把 ANR 从发生到处理的过程串起来。你可以看到,用户操作或系统事件是起点,主线程一旦超时,系统就会介入。

ANR 触发流程 用户操作 / 系统事件 主线程处理耗时任务 超过超时 阈值? 正常响应 系统检测到 ANR 弹出 ANR 对话框 用户点击等待 用户点击关闭 否 是 输入事件超时:5s 前台广播:10s Service:20s 后台广播:60s

这张图里,菱形节点是关键:系统不断检测主线程的响应时间,一旦超过阈值就进入 ANR 流程。值得注意的是,即使没有弹窗,某些「慢操作」也会在 log 里留下警告,这些信号同样不能忽视。

如何初步判断是否 ANR

在实际开发中,我们可以通过几个信号快速判断:

  • 用户反馈 App「卡死」「点不动了」——大概率是 ANR。
  • Logcat 中出现 ANR in ... 关键词。
  • Bugly、Firebase 等平台上报「ANR」事件。
  • trace 文件里看到主线程堆栈卡在 socketRead、FileInputStream.read、Thread.sleep 等阻塞方法上。

一句话总结:ANR 就是主线程「超时请假」,系统在用户体验和系统稳定之间做的强制干预。你只要记住那个超时阈值表,并养成「主线程别干重活」的习惯,就能避免 80% 的 ANR。