目录

Android ANR 原因、定位和避免

简介

ANR 全称 Application Not Responding,即应用程序无响应。

主要原因(为什么会发生 ANR?)

主线程(UI 线程)在规定时间内没有处理完相应的工作,就会出现 ANR。

ANR 场景

ANR 超时阈值表

类型 描述 前台 后台
Service 服务 20s 200s
Broadcast 广播 10s 60s
ContentProvider 内容提供者 10s
InputEventDispatching (常见) 输入事件分发(包括按键和触摸事件) 5s

以上四个条件的执行时间达到阈值,均可造成 ANR 的发生。

Android 中哪些操作是在主线程呢?

  1. Activity 的所有生命周期回调都是在主线程执行的。
  2. Service 默认在主线程执行。
  3. BroadcastReceiver 的 onReceive 回调是在主线程执行的。
  4. 没有使用子线程的 looper 的 Handler 的 handleMessage.post(Runnable) 是在主线程执行的。
  5. AsyncTask 的回调中除了 doInBackground,其他都是在主线程执行的。

定位 ANR

方法一:log

可以在 log 中搜索ANR inam_anr,该行会包含 ANR 的时间、进程以及何种 ANR 等信息。

方法二:traces.txt

使用adb命令

1
adb pull /data/anr/traces.txt

traces.txt 会保存到当前所在的目录下,从 trace.txt 文件查看调用 stack。

如何解决/避免 ANR?

  1. 使用 AsyncTask 处理耗时 I/O 操作
  2. 使用 Thread 或者 HandlerThread 提高优先级。(HandlerThread???)
  3. 使用 Handler 来处理工作线程的耗时任务。
  4. Activity 的 onCreate() 和 onResume() 回调中尽量避免耗时的操作。

    事实上,onPause() 中也应尽量避免耗时,可以放到 onStop() 中处理,这样可以让新的 Activity 尽快显示出来并切换到前台。

    【因为栈顶的 Activity 需要先 onPause 后(消失于前台),新的 Activity 才能启动。】

造成 ANR 的更多原因及解决办法

以上只是由于简单的主线程耗时造成的 ANR,实际上还有其他原因:

原因 解决办法
主线程阻塞或主线程数据读取(死锁/线程锁) 避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程 Query Provider,不要滥用 SharePreferences( SP 本章不展开讲)
CPU 满负荷,I/O 阻塞 文件读写或数据库操作放在子线程异步操作
内存不足 Manifest 中 设置 android:largeHeap=“true”,增大 App 分配的内存。但是不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。
各大组件 ANR 各大组件生命周期中也应避免耗时操作,具体参考上面的超时阈值。

参考链接: