1、ANR基础:到底什么是 ANR?
ANR,全称 Application Not Responding,就是「应用无响应」。说白了,你的 App 卡住了,系统觉得它「死了」,就会弹出一个对话框,问用户是要「等待」还是「关闭」。我在项目里见过不少新手,第一次遇到 ANR 时一脸懵,以为手机坏了。其实它背后有非常清晰的规则。
1.1 什么是 ANR?
当 Android 的主线程(UI 线程)被长时间阻塞,无法及时处理用户输入或系统消息时,系统就会判定这个应用处于无响应状态,进而触发 ANR。为什么主线程这么特殊?因为它承担了所有 UI 绘制、事件分发、生命周期回调等关键任务。你想想看,如果主线程在做一个耗时 10 秒的网络请求,用户点按钮就没反应了,系统当然不能忍。
核心原则: 所有可能耗时超过 5 秒的操作,都不该放在主线程。
1.2 ANR 的三种类型
根据触发场景不同,ANR 主要分三类。我当年排查第一个线上 ANR 时,就是靠这三类阈值定位的:
- 输入无响应(InputDispatcher) —— 用户触摸、按键等事件,系统分发后超时没有被处理。最常见的 ANR 类型。
- 广播超时(BroadcastReceiver) —— 广播接收器的 onReceive 方法执行时间过长。前台广播超时 10 秒,后台广播超时 60 秒。
- 服务超时(Service) —— 服务的生命周期方法(如 onCreate、onStartCommand)执行太久。前台服务 20 秒,后台服务 200 秒。
嗯,这里要注意:后台广播和服务的时间容忍度更大,但并不是说可以随便慢。我曾经在一个项目中,后台广播里做了数据库批量写入,结果直接导致 ANR。后来全改成了 WorkManager 异步处理。
1.3 ANR 阈值速查表
| 组件/场景 | 前台阈值 | 后台阈值 |
|---|---|---|
| 输入事件(InputEvent) | 5s | — |
| 广播(BroadcastReceiver) | 10s | 60s |
| 服务(Service) | 20s | 200s |
| ContentProvider 发布 | 10s | — |
为什么前台和后台阈值不同?因为前台应用直接影响用户体验,系统希望你快一点响应;后台应用可以稍微慢一些,但也不能无限拖。我建议你把这些阈值记在心里,写代码时脑子里时刻绷着这根弦。
1.4 核心知识图谱
下面这张图梳理了本章的脉络,方便你一眼看清 ANR 的基础结构:
1.5 避坑指南
- 不要在主线程做 I/O —— 文件读写、数据库查询、网络请求,统统扔到子线程。我曾经见过同事直接在 Activity.onCreate 里做 8MB 的文件解析,结果 ANR 必现。
- 广播接收器里别干重活 —— 就算后台广播有 60 秒,也不代表你可以放心做复杂计算。记得用 goAsync 或者转交给 IntentService 或 WorkManager。
- 服务的启动要快 —— 如果服务启动时需要初始化大量资源,考虑在 onStartCommand 里启动一个子线程,或者用 JobScheduler 延迟处理。
- 小心锁竞争 —— 主线程等待一个子线程持有的锁,也会触发 ANR。我在优化一个视频播放器时,就遇到过主线程等音视频解码锁导致 ANR 的情况。
切记: ANR 不像 Crash 那样会直接闪退,但用户会看到「应用无响应」对话框,体验极差。而且 Google Play 对 ANR 率有严格要求,高 ANR 率会直接影响应用评分和推荐。
1.6 小结
这一章我们搞清楚了三个核心点:
- ANR 就是主线程阻塞引发的无响应现象
- 它分输入、广播、服务三种类型,每种阈值不同
- 记住阈值,写代码时就知道哪些操作不能放主线程