Android中的ANR

ANR全称是Application Not Responding,即应用程序无响应,出现ANR主要原因是因为在主线程中做了太多耗时操作。

ANR产生的原因

我们都知道产生ANR是因为在主线程做了太多的工作,只有当应用程序的UI线程响应超时才会引发ANR,细分,超时产生原因一般有两种:
第一种:当前的事件没有机会得到处理,例如UI线程正在响应另外一个事件,当前事件由于某种原因被阻塞了。
第二种:当前的事件正在处理,但是由于耗时太长没能及时完成。
从本质上讲,产生ANR的原因有三种,大致可以对应到Activity/View,BroadcastReceiver和Service。

类型 原因
KeyDispatchTimeout View的按键事件或者触摸事件在特定的时间(5秒)内无法得到响应
BroadcastTimeout BroadcastReceiver的onReceive()函数运行在主线程中,在特定的时间(10秒)内无法完成处理
ServiceTimeout Service的各个生命周期函数在特定时间(20秒)内无法完成处理

常见的ANR问题场景

1.应用程序UI线程存在耗时操作,例如在UI线程中进行网络请求,数据库操作或者文件操作等,可能会导致UI线程无法及时处理用户输入等。
2.应用程序的UI线程等待子线程释放某个锁,从而无法处理用户的输入。
3.耗时的动画需要大量的计算工作,可能导致CPU负载过重。

ANR的定位和分析

当发生ANR时,我们可以通过结合Logcat日志和生成的位于手机内部存储的/data/anr/traces.txt文件进行分析和定位。
每次发生ANR时都会删除旧的traces文件,重新创建新文件。也就是说Android只保留最后一次发生ANR时的traces信息。
为了增强Android的异常信息收集管理能力,从2.2开始增加了DropBox功能,可以通过查看DropBox获取以前的ANR时的信息。
原理:
通过查看ActivityManagerService.java源码可知,里面通过调用AppErrors.java的appNotResponding方法将ANR信息存储到traces信息中,其中通过调用addErrorToDropBox方法负责生成dropbox文件,存储每次的traces信息,traces.txt只保存最后一次发送ANR的traces信息。
具体源码分析请参考
https://codezjx.github.io/2017/08/06/anr-trace-analytics/
使用:手机不需要root也可以获取traces.txt文件
在Studio的Terminal中执行以下命令获取traces.txt文件:
1.执行以下命令查看/data/anr/traces.txt是否存在:

adb shell(进入到手机系统目录)
cd /data/anr
ls

截图如下所示:
这里写图片描述
2.退出手机系统目录,使用pull命令获取traces.txt文件:

exit
adb pull /data/anr/traces.txt ./

截图如下所示:
Android中的ANR_第1张图片
dropbox需要DropBoxManagerService,关于dropbox的源码分析请参考:
http://gityuan.com/2016/06/12/DropBoxManagerService/
DropBoxManagerService服务的数据保存目录为/data/system/dropbox

ANR的检测

1.StrictMode:
严格模式StrictMode是Android SDK提供的用来检测代码中是否存在违规操作的工具类。
StrictMode主要检测两大类问题:
(1)线程策略ThreadPolicy:
detectCustomSlowCalls:检测自定义耗时操作。
detectDiskReads:检测是否存在磁盘读取操作。
detectDiskWrites:检测是否存在磁盘写入操作。
detectNetwork:检测是否存在网络操作。
(2)虚拟机策略VmPolicy:
detectActivityLeaks:检测是否存在Activity泄露。
detectLeakedClosableObjects:检测是否存在未关闭的Closable对象泄露。
detectLeakedSqlLiteObjects:检测是否存在Sqlite对象泄露。
setClassInstanceLimit:检测类实例个数是否超过限制。
其中ThreadPolicy可以用来检测可能存在的主线程耗时操作,解决这些检测到的问题能够减少应用发送ANR的概率。
注意:在Debug版本中使用,在市场版本中关闭掉。
使用方法:
在Application或者MainActivity类的onCreate方法中执行下面代码:

if(BuildConfig.DEBUG){
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
        }

其中penaltyLog表示在Logcat中打印日志,detectAll方法表示启动所有的检测策略。
根据具体需求只开启某些策略,代码如下

if(BuildConfig.DEBUG){
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork()
                    .penaltyLog()
                    .build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectActivityLeaks()
                    .detectLeakedSqlLiteObjects()
                    .detectLeakedClosableObjects()
                    .penaltyLog()
                    .build());
        }

2.BlockCanary:
BlockCanary是一个非侵入式的性能监控函数库,主要用来监控应用主线程的卡顿,而LeakCanary是用来监控应用的内存泄漏。
基本原理是利用主线程的消息队列处理机制,通过对比消息分发开始和结束的时间点来判断是否超过设定的时间,如果是,则判断为主线程卡顿。
使用参考官网:
https://github.com/markzhai/AndroidPerformanceMonitor

你可能感兴趣的:(android进阶读书笔记)