Android ANR 原因、定位和避免
目录
简介
ANR 全称 Application Not Responding,即应用程序无响应。
主要原因(为什么会发生 ANR?)
主线程(UI 线程)在规定时间内没有处理完相应的工作,就会出现 ANR。
ANR 场景
ANR 超时阈值表
| 类型 | 描述 | 前台 | 后台 |
|---|---|---|---|
| Service | 服务 | 20s | 200s |
| Broadcast | 广播 | 10s | 60s |
| ContentProvider | 内容提供者 | 10s | – |
| InputEventDispatching (常见) | 输入事件分发(包括按键和触摸事件) | 5s | – |
以上四个条件的执行时间达到阈值,均可造成 ANR 的发生。
Android 中哪些操作是在主线程呢?
- Activity 的所有生命周期回调都是在主线程执行的。
- Service 默认在主线程执行。
- BroadcastReceiver 的 onReceive 回调是在主线程执行的。
- 没有使用子线程的 looper 的 Handler 的 handleMessage.post(Runnable) 是在主线程执行的。
- AsyncTask 的回调中除了 doInBackground,其他都是在主线程执行的。
定位 ANR
方法一:log
可以在 log 中搜索ANR in或am_anr,该行会包含 ANR 的时间、进程以及何种 ANR 等信息。
方法二:traces.txt
使用adb命令
|
|
traces.txt 会保存到当前所在的目录下,从 trace.txt 文件查看调用 stack。
如何解决/避免 ANR?
- 使用 AsyncTask 处理耗时 I/O 操作
- 使用 Thread 或者 HandlerThread 提高优先级。
(HandlerThread???) - 使用 Handler 来处理工作线程的耗时任务。
- Activity 的 onCreate() 和 onResume() 回调中尽量避免耗时的操作。
事实上,onPause() 中也应尽量避免耗时,可以放到 onStop() 中处理,这样可以让新的 Activity 尽快显示出来并切换到前台。
【因为栈顶的 Activity 需要先 onPause 后(消失于前台),新的 Activity 才能启动。】
造成 ANR 的更多原因及解决办法
以上只是由于简单的主线程耗时造成的 ANR,实际上还有其他原因:
| 原因 | 解决办法 |
|---|---|
| 主线程阻塞或主线程数据读取(死锁/线程锁) | 避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程 Query Provider,不要滥用 SharePreferences( SP 本章不展开讲) |
| CPU 满负荷,I/O 阻塞 | 文件读写或数据库操作放在子线程异步操作 |
| 内存不足 | Manifest 中 |
| 各大组件 ANR | 各大组件生命周期中也应避免耗时操作,具体参考上面的超时阈值。 |
参考链接: