4. ANR触发机制与系统源码解析
ANR(Application Not Responding)并不是一个单一事件,而是系统在多个关键路径上对应用响应能力进行超时检测后,统一汇总到 ActivityManagerService 并弹出对话框的结果。理解 ANR 的关键,不是记住“主线程卡了 5 秒”,而是搞清楚四类 ANR 分别由谁计时、计时多久、在什么条件下判定、以及最终如何生成 traces 与日志。本节从系统源码视角拆解 Input、Broadcast、Service、ContentProvider 四类 ANR 的触发链路。
一、核心原理:四类 ANR 的计时主体与判定入口
Android 中 ANR 的判定入口集中在 ActivityManagerService(AMS)及其辅助类。不同组件由不同服务负责“喂狗”或“超时回调”,但最终都会调用 appNotResponding() 系列方法。四类 ANR 的计时主体如下:
- Input ANR:由
InputDispatcher检测输入事件分发超时,通过WindowManagerService回调到 AMS。 - Broadcast ANR:由 AMS 的
BroadcastQueue在派发广播时设置超时消息,前台/后台队列阈值不同。 - Service ANR:由 AMS 的
ActiveServices在serviceDoneExecuting未按时到达时触发。 - ContentProvider ANR:由 AMS 在
publishContentProviders阶段等待应用发布 provider 超时触发。
四类 ANR 的共同点是:系统在某个“必须由应用主线程完成”的环节设置了超时阈值,一旦超时且应用进程未处于可豁免状态(如调试中、已崩溃、正在关机等),就会判定 ANR。
二、四类 ANR 的触发条件与超时阈值
不同 Android 版本阈值略有差异,但整体稳定。下表以 AOSP 主线版本为参考,列出四类 ANR 的触发条件、默认阈值与关键源码位置。
| ANR 类型 | 触发条件 | 默认超时阈值 | 关键源码/类 |
|---|---|---|---|
| Input ANR | 输入事件分发后,应用在超时时间内未完成处理,且无新事件消费 | 5s(部分场景 8s) | InputDispatcher.cpp、InputManagerService |
| Broadcast ANR | 前台/后台广播接收器 onReceive() 未在阈值内执行完毕 |
前台 10s,后台 60s | BroadcastQueue.java、BroadcastReceiver |
| Service ANR | Service 启动、绑定或前台化后,未按时回调 serviceDoneExecuting |
前台 20s,后台 200s | ActiveServices.java、ServiceRecord |
| ContentProvider ANR | 应用进程启动后未在阈值内发布 ContentProvider | 10s | ActivityManagerService.java、ContentProviderRecord |
oom_adj / procState)。前台进程、可见进程通常按前台阈值判定;后台进程、空进程按后台阈值判定。因此同一个广播接收器,在不同进程状态下可能触发不同超时。
三、系统判定流程:从超时到 ANR 报告
以 Broadcast ANR 为例,源码链路大致如下:
// BroadcastQueue.java 简化逻辑
final void processCurBroadcastLocked(BroadcastRecord r, ProcessRecord app) {
r.receiver = app.thread;
r.curApp = app;
app.curReceiver = r;
// 设置超时消息
mHandler.sendMessageDelayed(
mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, r),
r.receiver.getType() == FOREGROUND ? BROADCAST_FG_TIMEOUT : BROADCAST_BG_TIMEOUT);
// 调用应用 onReceive
app.thread.scheduleReceiver(...);
}
// 应用执行完毕后回调
public void finishReceiver(IBinder who, int resultCode, ...) {
// 移除超时消息
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, r);
...
}
如果 finishReceiver 未在阈值内到达,BROADCAST_TIMEOUT_MSG 被触发,最终调用 ActivityManagerService.appNotResponding()。该方法会执行以下关键动作:
- 检查进程是否已处于 ANR 状态,避免重复报告。
- 检查是否处于调试豁免、崩溃恢复、关机等特殊状态。
- 向
/data/anr/traces.txt写入各线程堆栈,并抓取main、system_server等关键日志。 - 通过
AppErrors弹出“应用无响应”对话框,提供“等待”或“关闭”选项。 - 将 ANR 信息写入
dropbox与event log,供后续分析。
四、案例与误区复盘
案例:某应用在后台执行 BroadcastReceiver.onReceive() 时同步读取数据库,耗时约 15 秒。由于进程处于后台,系统按 60 秒阈值判定,未立即 ANR;但当用户再次打开应用,进程被提升为前台,后续广播按 10 秒阈值判定,立即触发 ANR。这说明“同一段代码是否 ANR”取决于进程状态与阈值。
常见误区:
- 误区一:认为所有 ANR 都是 5 秒。实际上 Input 是 5 秒,Broadcast 前台 10 秒、后台 60 秒,Service 前台 20 秒、后台 200 秒,ContentProvider 10 秒。
- 误区二:只看主线程堆栈。ANR 可能由 Binder 阻塞、锁竞争、系统服务卡顿引起,需要结合
traces.txt中多线程堆栈与system_server状态分析。 - 误区三:忽略
dropbox与event log。它们记录了 ANR 发生时的 CPU、内存、进程状态,是定位系统级卡顿的关键证据。
总结来看,四类 ANR 的触发机制可以归纳为:不同组件由不同系统服务计时,超时阈值随前后台状态变化,最终统一汇聚到 AMS 的 appNotResponding() 进行判定与报告。掌握这条链路,才能在日志与 traces 中快速定位是哪一个环节超时,从而对症下药。