注意:本篇文章是本人阅读相关文章所写下的总结,方便以后查阅,所有内容非原创,侵权删。
本篇文章内容来自于:
Android高级进阶 顾浩鑫
Android ANR产生的原因以及其定位分析
目录
- 什么是ANR
- ANR产生的原因
- 典型的ANR问题场景
- ANR发生了 如何定位和分析
- ANR的避免和检测
--5.1 StrictMode
--5.2 BlockCanary
1. 什么是ANR
ANR:Application Not Responding。
ANR的直观体验是用户在操作APP的过程中,感觉界面卡顿,当卡顿时间超过一定时间(一般5s)就会出现ANR对话框。
2. ANR产生的原因
主要是因为我们在主线程中做了太多的耗时操作。
超时产生原因一般有2种:
- 当前的事件没有机会得到处理。
例如UI线程正在响应另外一个事件,当前事件由于某种原因被阻塞了。 - 当前事件正在处理,但是由于耗时太长没能及时完成。
根据ANR产生的原因不同,超时时间也不尽相同。
从本质上讲产生ANR的原因有3种:
大致可以对应到Android四大组件中的三个(Activity/View、BroadcastReceiver、Service)
- KeyDispatchTimeout
最常见的一种类型,原因是view的按键事件或者触摸事件在特定的时间(5s)内无法得到响应。 - BroadCastTimeout
原因是BroadCastReceiver的onReceive()函数运行在主线程,在特定的时间(10s)内无法完成处理。 - ServiceTimeout
比较少出现的一种类型,原因是Service的各个生命周期函数在特定时间(20s)内无法完成处理。
3. 典型的ANR问题场景
- 应用程序UI线程存在耗时操作。
例如在UI线程中进行网络请求,数据库操作或者文件操作等,可能会导致UI线程无法及时处理用户输入等。
在Android4.0之后,如果在UI线程中进行网络操作,将会抛出NetworkOnMainThreadException异常。 - 应用程序的UI线程等待子线程释放某个锁,从而无法处理用户的输入。
- 耗时的动画需要大量的计算工作,可能导致CPU负载过重。
4. ANR发生了 如何定位和分析
当发生ANR时,可通过结合logcat日志和生成的位于手机内部存储的/data/anr/traces.txt文件进行分析和定位。
5. ANR的避免和检测
5.1 StrictMode
严格模式StrictMode是Android SDK提供的一个用来检测代码中是否存在违规操作的工具类。
主要用于检测两大类问题:(1)可能存在的主线程耗时操作 (2)是否发生泄漏
- 线程策略 ThreadPolicy
--detectCustomSlowCalls:检测自定义耗时操作
--detectDiskReads:检测是否存在磁盘读取操作
--detectDiskWrites:检测是否存在磁盘写入操作
--detectNetwork:检测是否存在网络操作 - 虚拟机策略 VmPolicy
--detectActivityLeaks:检测是否存在Activity泄漏
--detectLeakedClosableObjects:检测是否存在未关闭的Closable对象泄漏
--detectLeakedSqliteObjects:检测是否存在Sqlite对象泄漏
--setClassInstanceLimit:检测类实例个数是否超过限制
只能在Debug版本使用它,发不到市场上的版本要关掉。
使用
只需要在应用初始化的地方例如Application或者MainActivity的onCreate方法中执行代码:
if (BuildConfig.DEBUG) {
//===开启线程模式===
//开启全部
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
//开启部分
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());
//===开启虚拟机模式===
//开启全部
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
//开启部分
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectActivityLeaks().detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().build());
}