ANR触发原理:系统如何判定你的App"不响应"?
做ANR链路追踪,第一步得搞清楚——系统到底凭什么判定一个App挂了?这不是玄学,是Android系统里一套严格的超时+监控机制。
我记得刚接触ANR时,觉得它就是个弹窗提示。后来踩过几次坑才发现,背后是AMS(ActivityManagerService)和WMS(WindowManagerService)两套调度系统在实时盯着你的线程。一旦超时,直接给你"定罪"。
2.1 Android系统的ANR机制
系统怎么知道你卡住了?说白了,是通过消息队列超时检测。Android主线程本质是一个Looper循环,不断从MessageQueue取消息执行。如果某个消息处理太久,Looper就不再向Binder驱动发送心跳,系统就会认为"没响应"。
但实际检测更复杂。系统用了三套独立的超时监控器,分别对应不同的场景。我个人习惯把它们分为三类:
- InputDispatching Timeout(输入调度超时)——用户触摸、按键发到WMS,WMS等待App处理完成并反馈。
- Broadcast Timeout(广播超时)——AMS发送广播后,等待Receiver的onReceive()执行完成。
- Service Timeout(服务超时)——AMS启动Service后,等待其onCreate()或onStartCommand()返回。
你想想看,这三者本质都是"主线程忙不过来了"。但判定逻辑和超时阈值完全不一样,后文会详细讲。
2.2 AMS与WMS的决策流程
ANR判定不是简单倒计时。它是一套两段式决策流程:先由AMS或WMS发超时消息,再由系统进程回应"是否恢复"。我们来拆开看。
2.2.1 WMS:输入事件的ANR判定
用户手指一点,事件从驱动→InputReader→InputDispatcher→App主线程。WMS里的InputDispatcher会给App设一个超时计时器(默认5秒)。如果App在规定时间内没通过Binder通知"事件处理完毕",WMS就会上报ANR。
这里有个细节:只有输入事件才会触发WMS判定。如果App只是后台跑任务,不涉及输入,WMS就不管。
2.2.2 AMS:广播与服务超时
AMS扮演的是更宏观的监控者。它发送广播后,会启动一个Handler延时消息来模拟超时。如果Receiver在规定时间(前台广播10秒,后台广播60秒)内没有通过finishReceiver()通知完成,AMS就认为超时。
Service同理。启动Service时,AMS会设置一个超时(前台服务20秒,后台服务200秒)。如果Service的onCreate()或onStartCommand()没返回,就触发ANR。
2.2.3 决策流程图
2.3 超时时间阈值
阈值是多少?我直接列个表,建议你记下来。面试常考,压测排查也用得上。
| 场景 | 前台(秒) | 后台(秒) | 备注 |
|---|---|---|---|
| 输入事件(触摸/按键) | 5 | 5 | WMS判定,无前后台区分 |
| BroadcastReceiver (有序) | 10 | 60 | 从onReceive()开始计时 |
| ContentProvider超时 | 10 | 10 | 首次query()创建耗时 |
| Service启动 | 20 | 200 | onCreate/onStartCommand |
| DexClassLoader/热修复 | 15 | 15 | 系统内部阈值,不暴露 |
2.4 超时后的内部动作
触发ANR后系统做了什么?嗯,这里要注意:并不是立刻弹窗。系统会先做一系列"举证"动作:
- Dump主线程堆栈——写入
/data/anr/traces.txt或traces_xx.anr - Dump所有线程堆栈——包含native线程、Binder线程
- 收集内存信息——global reference count、锁信息等
- 写入dropbox——供开发者拉取分析
- 弹ANR对话框(仅前台App)或直接杀掉进程(后台App)
你会发现,那个对话框只是结果。真正有价值的数据在traces.txt里。我每次排查ANR第一件事就是拉这只"尸体"。
2.5 系统源码里的关键类
如果你喜欢看源码,记住这几个类:
com.android.server.wm.InputDispatcher—— WMS的输入超时逻辑com.android.server.am.ActivityManagerService—— 广播/服务超时处理com.android.server.am.AppNotRespondingDialog—— 弹窗生成com.android.server.am.AnrMonitor(Android 12+) —— 统一ANR监控
InputDispatcher的notifyANR()处打日志点,配合dumpsys window input看当前输入队列是否堵塞。这招在定位"触摸无响应但没弹框"的场景特别好用。
2.6 小结:ANR的本质是"主线程调度超时"
不管是输入、广播还是Service,系统判断的依据都一样:主线程在规定时间内没有完成应该做的事。背后是AMS和WMS两套独立计时器,它们共享一个前提——主线程的Looper必须能处理Binder回调。
所以,当你看到ANR时,别只盯着"卡顿"。先问自己:是哪条超时规则被触发了? 回答了这个问题,你就找到了断案的第一个线索。