ANR 概述与影响归纳总结
ANR 全称 Application Not Responding,说直白点,就是你的 App 卡死了,系统给了用户一个「强制关闭」的选项。我在做第一代埋点 SDK 时,就吃过 ANR 的大亏——当时因为主线程写文件阻塞,上线第一天就收到几十个 ANR 报警。
嗯,系统的判断逻辑其实不复杂:当主线程超过 5 秒没有处理输入事件,或者广播接收器 10 秒没回来,或者 Service 20 秒没完成,系统就会判定为 ANR。你想想看,如果你是用户,点一下按钮等 5 秒没反应,第一反应就是关掉这个 App 吧?
触发 ANR 的典型场景
根据我这些年踩过的坑,我把最常见的触发场景归了类。下面这个表格是基础判断标准,但我建议你记在脑子里:
| 类型 | 超时阈值 | 我的血泪教训 |
|---|---|---|
| 输入事件派发 | 5 秒 | 曾经因为在主线程做 SharedPreferences 写入,300 条数据卡了 7 秒 |
| 广播接收器(前台) | 10 秒 | 某次在 onReceive 里直接请求网络,结果 Wi-Fi 切换时直接 ANR |
| 广播接收器(后台) | 60 秒 | 后台广播其实更隐蔽,我遇到过第三方 SDK 在后台广播里做复杂计算 |
| Service | 20 秒 | Service 的 ANR 往往跟死锁有关,我就见过同步锁嵌套导致的永久阻塞 |
| ContentProvider | 10 秒 | 跨进程查询时如果 Provider 所在进程被阻塞,也会触发 ANR |
关键点:所有 ANR 的根因都是主线程被阻塞。但阻塞的原因千奇百怪——IO、锁竞争、Binder 调用、甚至是 CPU 负载过高导致的调度延迟。
ANR 对用户和业务的影响
影响其实分三层,越往上越致命:
- 用户体验暴跌:用户等着用,结果屏幕卡住。我亲眼见过用户卡了 5 秒后直接 Home 键强制退出的操作流。
- 应用被系统杀掉:系统会在 ANR 弹窗出现一段时间后直接 kill 进程,你的埋点还没上报就丢了。
- 用户流失与差评:这个最痛。据我了解的数据,单次 ANR 能导致次日留存下降 2%~5%。
所以我们在做埋点工程化时,第一条红线就是「埋点代码绝不能引起 ANR」。我自己曾因为在线程同步设计不当,导致埋点上报线程卡住主线程——那一次教训直接让我写了全套 ANR 监控链路。
注意:不要以为只有用户可见的卡顿才叫 ANR。像 Service 和后台广播的 ANR 用户看不到弹窗,但系统日志里会记录,同样影响应用状态。我们做数据保护时一定要把暗 ANR 也纳入追踪。
ANR 触发到影响的完整链路
下面这张图是我在项目里总结出来的完整链路。你看一遍就能理解为什么我们说「主线阻塞」是万恶之源。
实用技巧:我自己在排查 ANR 时,第一件事就是拿 trace 文件看主线程在做什么。如果看到「Thread-xxx blocked at ...」,基本就是死锁;如果是「pollOnce + 堆栈很长」,多半是 Binder 调用太久。记住这个套路,效率提升 50%。
ANR 的常见分类维度
为了方便后续做链路追踪,我把 ANR 按「触发源」和「可见性」做了分类。这个分类在我的埋点上报聚合逻辑中一直在用:
- 可见 ANR:用户能看到弹窗(输入事件超时、前台广播超时)。这类 ANR 直接伤害用户感知,需要最高优先级治理。
- 不可见 ANR:后台广播、Service、ContentProvider 超时。用户看不到弹窗,但系统日志会生成,且可能杀掉后台服务。
- 主线程阻塞:一切 ANR 的根因,但有些阻塞没触发超时阈值,属于「准 ANR」——我通常把主线程卡顿超过 3 秒的也纳入监控。
总结影响:ANR 不仅仅是技术问题,更是业务问题。我自己测算过:一个 Top 级的 App 如果 ANR 率超过 0.5%(每 100 次启动就有 0.5 次 ANR),日活损失就可能达到 5 位数。所以我们做数据保护的第一步,就是先管好主线程。