Android ANR 分析
一、先把 ANR 想成一件事
把 App 想成一条单车道公路:
- 主线程 = 唯一车道
- 所有 UI 操作 = 必须走这条路
- ANR = 这条路被堵住太久
二、ANR 分析只做一件事
找到:谁把这条路堵住了
三、实战第一步:先看 traces(90% 问题在这里解决)
你打开 ANR 文件,第一件事:
👉 找 main 线程
"main"
👉 看最后一行在干嘛
at xxx.xxx()
这一行非常重要:
它就是“卡住的现场”
四、只用四种方式理解所有 ANR(核心)
看到 main 线程栈,不要复杂化,只归类成四种:
① 在干活(自己慢)
画面:主线程在搬砖
表现:
- IO(文件 / 数据库)
- 网络请求
- JSON / Bitmap 处理
👉 栈特点:RUNNABLE,一直在执行
结论:
事情没错,但不该在主线程做
② 在等门(等锁)
画面:主线程在门口等钥匙
表现:
waiting to lock
👉 核心不是主线程,而是:
谁拿着锁不放
③ 在打电话(等结果)
画面:主线程在等别人回复
表现:
- WAITING
- Binder
- join / wait
结论:
把“未知时间的事情”当成同步做了
④ 前面堵车(队列问题)
画面:前面车太慢,后面全堵住
表现:
- 当前栈看起来不重
- 但 UI 还是卡
结论:
前一个任务拖慢了整个消息队列
五、线程状态怎么用
线程状态只做一件事:
帮你快速判断方向
| 状态 | 含义 | 方向 |
|---|---|---|
| RUNNABLE | 正在执行 | 看是否耗时 |
| BLOCKED | 等锁 | 查锁 |
| WAITING | 等结果 | 查依赖 |
⚠️ 注意:
状态不告诉你原因,只告诉你类型
六、什么时候看 log(非常关键)
很多人误区是:一上来就看 log。
正确顺序是:
✔ 先看 traces(定位“卡在哪里”)
✔ log 只在三种情况看:
1️⃣ 栈看不出明显耗时
比如 main 在 MessageQueue
2️⃣ 要还原“发生顺序”
例如:
点击 → 跳转 → 加载 → 卡死
3️⃣ 怀疑“前面已经埋雷”
比如:
前一个页面已经慢了,但在这里爆发
👉 一句话:
traces 看“卡点”,log 看“过程”
七、一眼判断法(真正实用)
看到 main 栈,只问一句:
这件事,如果放在后台做,会不会更合理?
- ✔ 会 → 主线程写错了
- ❌ 不会 → 设计本身有问题
八、一个完整分析例子(帮助你建立直觉)
traces:
main:
at DataManager.getData()
waiting to lock
你看到什么:
- 主线程在 getData
- 在等锁
下一步思考:
- 谁持有锁?
- 持锁线程在干嘛?
结论:
主线程等待锁,但锁被子线程持有且执行耗时操作,导致 ANR
九、最终收敛(核心一句话)
ANR = 主线程被“做事”或“等待”卡住了
十、最重要的一句总结
先看栈找卡点,再用逻辑判断“这件事是否应该发生在主线程”