ANR 全称Application Not Responding(应用程序无响应),一般页面卡顿时间超过(一般是5秒)一定时间就会出现ANR对话款。Logcat一般会发现ANR以及traces.txt等字样。出现ANR主要原因是因为我们在主线程中做了太多耗时操作。
A、ANR产生的原因
只有当应用程序的UI线程相应超时才会引起ANR,超时产生原因一般有两种
(1)当前的事件没有机会得到处理,例如UI线程正在响应另外一个事件,当前事件由于某种原因被阻塞了。
(2)当前事件正在处理,但是由于耗时太长没能及时完成
根据ANR产生的原因不同,超时事件也不尽相同,从本质上讲,产生ANR的原因有三种,大致可以对应到Android 中四大组件中的三个(activity/view,BroadcastReceiver和service)。
(1)KeyDispatchTimeOut类型
最常见的一种类型,原因是View的按键事件或触摸事件在特定的事件(5秒)内无法得到响应
(2)BroadcastTimeOut类型
原因是BroadcastReciver的onReceive()函数运行在了主线程中,在特定的事件(10秒)内无法完成处理
(3)ServiceTimeOut类型
比较少出现的一种类型,原因是Service的各个生命周期函数在特定时间(20秒)内无法完成处理
B、典型的ANR问题场景
(1)场景一:
应用程序UI线程存在耗时操作,例如在UI线程中进行网络请求,数据库操作或者文件操作等,可能会导致UI线程无法及时处理用户输入等。当然在Android4.0之后,如果在UI线程中进行网络操作,将会抛出NetworkOnMainTreadException异常。
(2)场景二:
应用程序的UI线程等待子线程释放某个锁,从而无法处理用户的输入。
(3)场景三:
耗时的动画需要大象的计算工作,可能会导致CPU负载过重
C、ANR的定位和分析
当发生ANR时,可以通过结合Logcat日志和生成的位于手机内部存储的/data/anr/traces.txt文件进行分析和定位。
(1)Logcat日志信息
查看日志
(2)traces.txt日志信息
有助于问题已定位的信息主要内容
a、发生ANR的进程名称、ID,以及时间。
b、其他
QQ图片20180921175250.pngc、主工程基本信息
QQ图片20180921175347.pngd、主线程的详细信息
QQ图片20180921175423.pnge、线程的调度信息
QQ图片20180921175502.pngf、线程的上下文信息
QQ图片20180921175543.pngg、线程的堆栈信息
QQ图片20180921175624.pngD、ANR的避免和检测
(1)不要在主线程中做耗时操作
为了避免开发中可能发生的ANR的问题,除了切记不要再主线程中做耗时操作,我们也可以借助一些工具来进行检测,从而更有效的避免ANR的引入
(2)StrictMode(代码检测)
严格模式StrictMode 是Android SDK提供的一个用来检测代码是否存在违规操作的工具类,StrictMode 主要检测两大类为题
a、线程策略ThreadPolicy
检测可能存在的主线程耗时操作,解决这些检测到的问题能够减少应用发生的ANR的概率。需要注意的是我们只能在DEbug版本中使用它,发布到市场版本必须关掉。
QQ图片20180921175750.png
b、虚拟机策略VmPolicy QQ图片20180921175827.png
c、StrictMode使用
使用很简单,我们只需要在应用初始化的地方例如Application或者MainActivity类中onCreate方法中执行如下代码
@Override
protected void onCreate(Bundle savedInstanceState) {
if (BuildConfig.DEBUG) {
/**开启线程模式*/
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
/**开启虚拟机模式*/
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_anr);
}
调用detectAll表示检测所有的检测策略,我们也可以根据应用需求只开启一些策略
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void onCreate(Bundle savedInstanceState) {
if (BuildConfig.DEBUG) {
/**开启线程模式,某些策略*/
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectCustomSlowCalls()
.penaltyLog()
.build());
/**开启虚拟机模式,某些策略*/
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectLeakedRegistrationObjects()
.penaltyLog()
.build());
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_anr);
}
BlockCanary(非侵入式性能监控函数库)
BlockCanary是一个非侵入时的性能监控函数库,他的用法和LeakCanary类似,只不过LeakCanary检测内存泄漏,而BlockCanary主要用来监控应用主线程的卡顿,它的基本原理利用主线程的消息队列处理机制,通过对比消息分发开始和结束的事件点来判断是否超过设定的时间,如果是,则判断为主线程卡顿。
集成顺序:
a、build.gradle添加依赖
//主线程卡顿监控依赖
implementation 'com.github.moduth:blockcanary-android:1.5.0'
//如果仅在debug 包启用BlockCanary 进行卡顿监控和提示的话,这样写
//debugCompile 'com.github.moduth:blockcanary-android:1.5.0'
//releaseCompile 'com.github.moduth:blockcanary-no-op:1.5.0'
b、在Application中调用
@Override
public void onCreate() {
super.onCreate();
/**初始化调用*/
BlockCanary.install(this,new AppBlockCanaryContext()).start();
}
public class AppBlockCanaryContext extends BlockCanaryContext{
/**
* 实现各种上下文,包括应用标识符、用户uid、网络类型、卡曼判断阈值、Log保存位置等
*/
}
网友评论