一、什么是ANR?
ANR(Application Not Responding),中文意指程序未响应——当应用不能够灵敏地响应用户的操作时(应用响应不及时),ANR就会出现,这时系统会向用户弹出一个提示框,让用户选择继续等待或确定关闭应用,示例如下图:
一个合理的应用程序中不应该出现ANR,ANR频繁出现会导致用户体验变差、用户留存率低等问题。
二、为什么会出现ANR?
在Andorid上,系统会通过Activity Manager和Window Manager服务来监控应用的响应情况,如果应用响应超出限定时间了,为了避免对用户的体验造成困扰,系统就会弹出ANR提示供用户选择是否继续等待应用响应。
什么情况下会导致应用响应不及时(出现ANR)?
-
主线程阻塞
在主线程中做了大量的耗时操作,如网络请求;频繁/大批量的数据操作等。 -
CPU满负荷工作时进行I/O操作
当CPU使用率达到100%,在应用内仍在主线程上进行频繁的读写操作时,就会导致ANR的产生。 -
内存不够用
系统分配给每个App的可用内存是有限的,如果App内存在内存泄漏等情况的话,这个是会导致ANR的出现的。
三、ANR的种类
ANR的种类有以下三种:
- 主要类型:KeyDispatchTimeout(5 seconds)
按键或触摸事件在特定时间(5秒)内无响应。
- 小概率类型:BroadcastTimeout(10 seconds)
BroadcastReceiver.onReceive()在特定时间(10秒)内无法处理完成。
- 小概率类型:ServiceTimeout(20 seconds)
Service各个生命周期函数在特定的时间(20秒)内无法处理完成。
四、什么情况下会产生ANR?
-
出现ANR的前提条件
在Android应用中,ANR并不是随意出现的,它的出现也是有前提条件限制的,大致为以下几条:- 只有主线程(UI线程)才会产生ANR;
- 只有某些特定的操作或者触摸事件才会导致ANR的出现(如:按键或触摸事件、Service或BroadcastReceiver内做耗时操作等);
- 在上述条件情况下,系统会监测对应的操作是否及时,不同的情况下限制事件也是不一致的:
- 主线程(UI线程)对触摸、输入事件在5秒内没有处理完毕(如:键盘输入、触摸屏幕等);
- 主线程在执行BroadcastReceiver的onReceive()函数时10秒内没有处理完毕;
- 主线程在Service的各个生命周期函数在20秒内没有处理完毕。
除此之外,只有在CPU满负荷工作时做耗时I/O操作、内存不够用的情况下才会产生ANR。
-
出现ANR情况
- 在主线程中进行耗时的网络请求、大批量的数据读写、数据库操作;
- Service各个生命周期方法内进行和耗时操作,在20s内未处理完毕;
- BroadcastReceiver内进行了耗时操作,超过了10s的限制时间;
- 其它线程持有锁,导致主线程不能够响应;
- 调用Thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候;
- CPU满负荷时仍进行大量的I/O操作;
- 应用内内存泄漏导致的响应不及时。
五、如何预防ANR的产生?
其实说来说去,导致ANR发生的罪魁祸首就是在主线程中进行了耗时操作,那么在常规情况下,我们可以通过以下几种方式来防止ANR的产生:
-
主线程(UI线程)做耗时操作引发ANR预防
- 不在主线程(UI)线程中做耗时操作,开辟单独的子线程来处理耗时阻塞事务.;
- 不在Service、BroadcastReceiver内做耗时操作;
- 不乱用Thread的join()、sleep()、wait()方法。
-
CPU满负荷工作时进行I/O操作情况下预防
这种情况一般还是I/O操作在主线程上进行导致的,同理可以通过开启子线程的方式解决。 -
内存不足产生的ANR
排查内存泄漏解决内存不够用的情况,推荐使用LeakCanary。
六、查看、分析ANR信息
-
获取ANR产生的traces.txt文件
当发生ANR后,系统会在data/anr/目录下生成一个名为traces.txt的文件,文件内主要记录了ANR产生时系统信息的一些情况,我们可以通过adb命令的方式来将文件导出至本地:
adb pull data/anr/traces.txt 导出后的路径
-
traces.txt文件内容分析
打开traces.txt文件后我们会看到如下图内容:
这里我已经标出了问题产生位置、原因以及应用包名,关于traces.txt内具体信息的查阅,可以参考
traces.txt Log分析。
未完待续
新手上路,欢迎指正批评。