3. ANR的触发机制与阈值归纳总结
ANR 这个词,干 Android 的没人不认识。说白了就是「应用无响应」——你的 App 卡住了,系统弹个对话框问用户「等还是关」。但你真的清楚它到底怎么触发的吗?阈值又分别是多少?我在项目里排查过好几次 ANR,发现很多人连基础阈值都记混。今天咱们就捋清楚。
触发机制:谁在盯着你?
系统里有个叫 ServiceManager 的看门狗,但不是它直接触发 ANR。真正干活的是一组 Watchdog 线程和 AMS(Activity Manager Service)。它们给主线程发一条消息,然后开始倒计时——
- 如果主线程在规定时间内处理完消息,平安无事。
- 如果超时还没处理完 → 系统认为线程被堵死了 → 弹出 ANR 对话框。
嗯,这里要注意:不是只有主线程做耗时操作才触发。比如 BroadcastReceiver 在 onReceive 里跑网络请求,或者 Service 在 onStartCommand 里循环计算——只要超时,一样会挂。
核心结论: ANR 本质上是「系统设定了一个 deadline,执行方没赶在 deadline 前交出结果」。
阈值清单:分别多少毫秒?
我把不同组件的阈值整理成了表。我自己经常拿来贴在工位上——记不住就翻。
| 组件 / 场景 | 超时阈值 (Android Q+ 默认) | 说明 |
|---|---|---|
| BroadcastReceiver (前台) | 10 秒 | 从 onReceive 开始倒计时,10 秒内必须返回 |
| BroadcastReceiver (后台) | 60 秒 | Android 8.0 后后台广播限制更严,但阈值仍 60s |
| Service (前台启动) | 20 秒 | onStartCommand / onCreate 含耗时操作容易中招 |
| Service (后台启动) | 200 秒 | 其实很长了,但网络重试或数据库大事务还是可能超 |
| ContentProvider | 10 秒 | 查询或插入时主线程卡住的话 |
| Input 事件 (点击/触摸) | 5 秒 | 最容易被用户感知,5 秒没响应就弹框 |
| KeyDispatchTimeout | 5 秒 | 按键事件,和 Input 事件走同一套逻辑 |
我个人的习惯: 别死记硬背这些数字。你只要记住一个原则——主线程不做任何 I/O、网络、复杂计算、锁等待。阈值只是底线,你应该比底线早几倍完成。
触发流程:一条消息引发的血案
系统内部是怎么一步步判定 ANR 的?我曾经跟过源码,画了张流程图,你看一眼就明白。
几个容易踩的坑
- BroadcastReceiver 里做异步操作:很多人觉得我开个线程就不超时了。错!
onReceive返回后系统就认为处理完了,你那个线程跑多久它不管。但如果你在onReceive里直接做耗时任务,10 秒超时一定炸。 - ContentProvider 的 query 在 UI 线程执行:我遇到过 Room 在主线程查数据,结果磁盘 I/O 卡了 8 秒,正好撞上 ContentProvider 超时。其实
allowMainThreadQueries()是罪魁祸首。 - Input 事件 5 秒为什么最严格? 因为用户感受最直接——点一下屏幕,手机半天没反应。其他组件的超时用户可能感知不到,但触摸无响应立马就会点「关闭」。
我曾经踩过的雷: 在 Service 的
onStartCommand 里用 Thread.sleep(15000) 模拟网络等待——前台启动 20 秒阈值看似安全,但当多个 Intent 连续到来时,每个 onStartCommand 串行执行,累计时间轻易超过 20 秒。我那次是后台推送批量处理,ANR 日志里 Service 耗时显示 35 秒……教训就是:有任何长时间操作都丢到工作线程,或者用 IntentService / JobIntentService。
小结
ANR 的触发机制说复杂也复杂,说简单也简单——就是系统给你设了个闹钟,到点了你还赖在床上,它就告状。不同组件的阈值从 5 秒到 200 秒不等,但我建议你统一按照 5 秒的标准来写代码:只要是主线程,任何操作超过 5 秒都算高危。
你想想看,Retrofit 网络请求超时我们通常设 10 秒、30 秒,如果放在主线程发起同步请求,那就等于埋了一颗定时炸弹。后面我会专门分析 Retrofit 超时与 ANR 的关联,今天先把阈值和触发流程吃透。