1、ANR 概念与原理
ANR 全称是 Application Not Responding —— 应用无响应。说白了,就是你的 App 主线程被卡住了,系统等得不耐烦,直接弹个对话框问用户「要继续等还是关掉」。我最早接触这个概念是在做某个社交 App 的聊天列表优化时,用户滑动不快,但偶尔就会弹 ANR,当时还以为是机型问题……后来才发现根本原因是自己埋的坑。
嗯,要理解 ANR,先得知道系统在「等什么」以及「等多久」。Android 对不同类型的阻塞设了不同的超时阈值,超过这个时间,就会判定为 ANR。
触发场景
官方定义的三大触发场景,我整理成一张表,方便你对照着记:
| 场景类型 | 超时时间 | 触发条件 |
|---|---|---|
| 输入事件 | 5 秒 | 主线程未处理完 Input 事件(如触摸、按键) |
| 广播 | 10 秒 | BroadcastReceiver 的 onReceive() 未执行完毕 |
| 服务 | 20 秒 | Service 的 onCreate() / onStartCommand() / onBind() 未执行完 |
这里有个细节:广播的 10 秒指的是前台广播。如果是后台广播,系统会放宽到 60 秒,但同样会触发 ANR。服务也一样,前台服务 20 秒超时,后台服务 200 秒。不过你记住核心阈值就行,实战中我们按最严格的来监控。
系统底层的检测机制
Android 是怎么知道你主线程卡住 5 秒的?靠的是「消息队列 + 监视器」这套组合拳。我拆成几步来讲:
- 架构层:每个应用的主线程背后是一个 Looper 循环。Looper 不断从 MessageQueue 里取消息,交给 Handler 处理。
- 埋点层:系统在 Input 事件派发前,会在 Looper 中插入一个「超时监视消息」。如果 5 秒内这个监视消息没有被取出(说明前面的消息执行太久),系统就判定超时。
- 上报层:AMS(Activity Manager Service)会收到超时通知,然后执行以下动作:暂停当前组件、生成 traces.txt、弹出 ANR 对话框。
- 日志层:Traces 文件会 dump 当前所有线程的堆栈,包括主线程在哪个方法卡住。这就是我们分析 ANR 的第一手证据。
注意: 我曾在某款直播 App 中遇到一个奇怪的 ANR:每次切后台回来就弹。后来发现是 BroadcastReceiver 里做了网络请求,而网络请求居然跑在主线程。广播的超时时钟在 onReceive() 开始前就已经启动了,你还没来得及切线程,时间就到了。
核心要点: ANR 的检测不是「实时监控」,而是「到点触发」。系统在每个组件启动前设置一个定时器,如果定时器到期时组件还没结束,就判定 ANR。
下面是整个检测流程的示意图,画清楚系统做了哪些事:
底层实现细节
说到 WatchDog,我记得早期 Android 版本里 SystemServer 内部有个专门的线程叫 Watchdog,它会定期检查各个服务是否还活着,如果发现主线程卡死超过阈值,就直接杀进程。现在 Android 把检测逻辑做得更分散了:
- Input 事件:由 InputDispatcher 的 input event timeout 机制触发,超时后发消息给 AMS。
- 广播:AMS 在广播分发时注册一个 BroadcastQueue 中的超时 Alarm,到点未完成就触发 ANR。
- 服务:ActiveServices 类中维护一个 Service 超时列表,每次服务启动时设置一个延迟 Runnable。
说白了,这些都是基于系统 Handler 的延迟消息。你想想看,既然系统能用 Handler 来检测超时,那我们的监控不也可以模仿一套类似的机制吗?没错,后面章节讲 ANR 监控方案时,我会专门说怎么自己写一个轻量的检测器。
一个小经验: 我不建议你把保活或监控逻辑放在主线程,因为那本身就是 ANR 的源头。我习惯在另一个进程里做监控,比如用 Service 或者 JobScheduler,这样就算主进程卡死,监控进程还能上报。
最后,再来个总结:ANR 的本质就是「主线程没有及时响应系统发起的某个任务」。它的检测机制可以概括为「定时器+回调」,而分析 ANR 的核心就是看 traces 文件里主线程在干什么。后面几章我们会实战解剖 traces,你到时候就知道这堆堆栈有多直白了。