动态交付性能监控与ANR治理案例 - 第1节
一、什么是ANR?你真的了解它吗?
ANR,全称 Application Not Responding。翻译过来就是“应用无响应”。
说白了,就是你的 App 僵住了。系统看你 5 秒内没处理完输入事件,直接弹个对话框问你“继续等待还是关闭?”。我刚开始做开发时,觉得 ANR 离自己很远。直到有一次,用户反馈登录页面点不动,我一看日志,好家伙,主线程在读文件——典型的“不准时响应”。
1.1 ANR 的触发条件
系统判定 ANR,主要看这三个场景:
- 输入事件(点击、滑动)响应超过 5 秒
- BroadcastReceiver 的 onReceive() 执行超过 10 秒(前台)/ 60 秒(后台)
- 前台 Service 的 onStartCommand() 执行超过 20 秒
你想想看,主线程一旦被阻塞,整个 UI 就停摆了。系统可不管你是在等网络还是算大数,超时就弹窗。我自己的经验是,多数 ANR 并不是单一原因造成的,而是“多种阻塞叠加”。
1.2 动态交付场景下,ANR 更容易出现
动态交付模式,说白了就是“边跑边下载代码和资源”。这种模式天然就有几个“暗坑”:
- 网络强依赖:代码热修复包、动态资源包的下载,一旦遇上网速卡顿,主线程如果同步等,必然 ANR
- 资源竞争:多个插件或模块同时尝试加载,抢占主线程的 Looper 时间片
- 兼容性风险:动态下发的代码可能与宿主环境有冲突,直接导致主线程死锁
我记得有个项目,插件化框架在 App 启动时动态加载一个 so 文件,结果文件下载超时,主线程直接卡死了。这就是典型的动态交付 ANR。
二、为什么动态交付必须做性能监控?
没有性能监控,你就是在“盲人摸象”。用户投诉 ANR,你才去翻日志,到那时黄花菜都凉了。
我个人习惯是:把监控做在问题发生之前。你不需要等到 ANR 弹窗,从卡顿的那一刻就应该被捕获。
2.1 性能监控的核心价值
| 监控维度 | 关键指标 | 监控目标 |
|---|---|---|
| 主线程卡顿 | 响应时间、帧率 | 在 ANR 弹窗前 2 秒预警 |
| 资源加载 | 下载耗时、解压耗时 | 检测“慢加载”链路 |
| 内存占用 | 堆内存、GC 频率 | 避免因 GC 导致主线程停顿 |
| 代码执行 | 方法耗时、锁竞争 | 定位“谁占着主线程不放” |
2.2 监控与治理的关系图
很多人以为监控就是“记日志”,不是的。监控是“眼睛”,治理是“手术刀”。下面这张图展示了动态交付场景下,ANR 从发生到被监控治理的全链路关系:
2.3 实战中如何快速定位 ANR 源头
嗯,这里我要分享一个小技巧。当你在 adb logcat 中看到 ANR 弹窗时,第一时间拉取 /data/anr/traces.txt:
adb pull /data/anr/traces.txt
这个文件里记录了所有线程的堆栈信息。我一般会先看“main”线程的标志,找到它到底卡在哪个方法上。如果发现是动态交付模块的方法,立刻定位到对应的插件或资源加载链路。
我曾经在一个案例里,看到 main 线程阻塞在 PluginLoader.loadPlugin() 方法上。进一步翻看 traces 文件,发现它在一个 synchronized 块里等另一个线程释放锁。那个线程却在等网络下载完成——典型的死锁型 ANR。
所以结论很清晰:动态交付的性能监控,不只是监控主线程,还要监控资源加载、锁竞争、网络延迟。这些因素一交织,ANR 就出来了。