继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android ANR

慕标5832272
关注TA
已关注
手记 1222
粉丝 229
获赞 1001

产生的原因及其定位分析


          ANR 是Android中一个独有的概念,每一本介绍Android开发的入门书几乎都会对其进行介绍,它的全称是Application Not Responding(应用程序无响应),对于高质量的代码,ANR在开发者自测过程中可能不会经常遇到,但一旦测试人员进行Monkey测试,AND出现的概率就比较高了,如何快速分析定位并解决,是开发者的必修课。

          ANR的直观体验是用户在操作APP的过程中,感觉界面卡顿,比如按下某个按钮,打开某个页面等,当卡顿超过一定时间(一般是5秒)时就会出现ANR对话框。

          这时查看Logcat,一般可以发现ANR以及trace.txt等字样,后面会详细介绍,可以发现出现ANR主要是因为我们在主线程做了太多耗时操作,这时你可以选择等待按钮,等待应用程序结束主线程的耗时操作,或者选择确定按钮,结束这个应用程序,ANR对于一个应用来说是不能承受之通,其影响并不比应用发生Crash小。

ANR产生的原因


只有当应用程序的UI线程响应超时才会引起ANR,超时产生原因一般有两种。

1 当前的事件没有机会得到处理,例如UI线程正在响应另外一个事件,当前事件由于某种原因被阻塞了。

2 当前的事件正在处理,但是由于耗时太长没能及时完成。

根据ANR产生的原因不同,超时时间也不尽相同,从本质上讲,产生ANR的原因有三种,大致可以对应到Android 中四大组件中的三个(Activity/View,BroadcastReceiver和Service)。

KeyDispatchTimeOut :最常见的一种类型,原因是View的按键事件或者触摸事件在特定的时间(5秒)内无法得到响应。

BroadcaseTimeOut:原因是BroadcastReceiver的onReceiver()函数运行在主线程中,在特定的时间(10秒)内无法完成处理。

ServiceTimeOut:比较少出现的一种类型,原因是Service的各个生命周期函数在特定时间(20秒)内无法完成处理。

典型的ANR问题场景


1 应用程序UI线程存在耗时操作,例如在UI线程中进行网络请求,数据库操作或者文件操作等,可能会导致UI线程无法及时处理用户输入等,当然在Android 4.0之后,如果在UI线程中进行网络操作,将会抛出NetworkOnMainException异常。

2 应用程序的UI线程等待子线程释放某个锁,从而无法处理用户的输入

3 耗时的动画需要大量的计算工作,可能导致CPU负载过重

ANR的定位和分析


            当发生ANR时,开发者可以通过结合Logcat日志和生成的位于手机内部存储的/data/anr/traces.txt文件进行分析和定位。

1 Logcat日志信息

下面我们通过在主线程中模拟一个耗时操作来使应用发生ANR,除了在Logcat中可以看到生成traces.txt文件的日志外。

webp

webp

同时如果由于ANR导致应用发生崩溃,那么除了打印出上面的信息之外,还会调用CrashAnrDetector打印出下面的信息:

webp

可以看到,Logcat日志信息中主要包含如下内容:

1 导致ANR的类命及所在包名:MainActivity,com.asce1885.anrdemo

2 发生ANR的进程名称及ID: com.asce1885.anrdemo,8672

3 ANR产生的原因(类型):Input disptching time out ,属于KeyDispatchTimeOut类型

4 系统中活跃进程的CPU占用率:1.3% 8672/com.asce1885.anrdemo:0.7% user+0.6% kernel/faults:7276 minor 17 major.

2 Traces.txt日志信息

         从Logcat 的日志信息我们可以知道引发ANR的具体的类信息,以及ANR的类型,但是这不足够开发者定位到具体引发问题的代码行,为了获得进一步的信息,我们需要借助于ANR过程中生成的堆栈信息文件/data/anr/traces.txt ,这个文件可以通过终端Termianl中执行adb pull命令从手机的内部存储中拷贝到电脑中,也可以通过如下语句拷贝到MacBook Pro的桌面上

webp


webp

webp

可以看到 traces.txt文件有助于问题定位的信息主要包括如下内容

发生ANR的进程名称,ID,以及时间

手机的CPU架构:arm

堆内存信息:24%free ,16MB/21MB,53963object

主线程基本信息:

线程名称=”main“  线程优先级:prio=5 

线程锁ID:tid=1   线程状态:Sleeping

主线程的详细信息

线程组名称:group =”main“

线程被挂起的次数:sCount=1

线程被调试器挂起的次数:dsCount=0,当线程被调试结束后,sCount会被重置为0,它的值会重新根据是否被挂起而增加,而dsCount不会被重置为0,因此,dsCount可以用来判断这个线程是否被调试器调试过。

线程的Java对象地址:obj=0x87789ef0

线程本身的Native对象地址:self=0xb4f07800

线程的调度信息

Linux系统中内核线程ID:sysTid=8672,可以看到主线程的线程号和进程号相同

线程调度优先级:nice=0

线程调度组:cgrp=apps

线程调度策略和优先级:sched=0/0

线程处理函数地址:handle=0xb6fe4ec8

线程的上下文信息

线程调度状态:state=s

线程在CPU中的执行时间,线程等待时间,线程执行的时间片长度:schedsta=(...)

线程在用户态中调度时间值:utm=18

线程在内核态中的调度时间值:stm=6

最后执行这个线程的CPU核序号:core=3

线程的堆栈信息

堆栈地址和大小:stack=... stackSize=8MB

堆栈信息:从中可以看到,ANR是由于在MainActivity类中的triggerAnrWithLongOperation函数调用了Threadsleep导致

ANR的避免和检测


为了避免在开发中引入可能导致应用发生ANR的问题,除了切记不要在主线程中作耗时操作,我们也可以借助于一些工具来进行检测,从而更有效的避免ANR的引入

StrictMode

严格模式StrictMode是Android SDK提供的一个用来检测代码中是否存在违规操作的工具类,StrictMode 主要检测两大类问题

1 线程策略ThreadPolicy:

detectCustomSlowCalls:检测自定义耗时操作

detectDiskReads:检测是否存在磁盘读取操作

detectDiskWrites:检测是否存在磁盘写入操作

detectNetwork:检测是否存在网络操作

2 虚拟机策略VmPolicy

detectActivityLeaks:检测是否存在Activity泄漏

detectLeakedClosableObjects:检测是否存在未关闭的Closable对象泄漏

detectLeakedSqliteObjects:检测是否存在Sqlite对象泄漏

setClassInstanceLimit:检测类实例个数是否超过限制

         可以看到,其中的ThreadPolicy可以看来检测可能存在的主线程耗时操作,解决这些检测到的问题能够减少应用发生ANR的概率,需要注意的是,我们只能在Debug版本中使用它,发布到市场上的版本要关闭掉,StrictMode的使用简单,我们只需要在应用初始化的地方例如Application或者MainActivity类的onCreate 方法中执行如下代码即可

webp

上面的初始化代码调用penaltyLog表示在Logcat中打印日志,调用detectAll方法表示启动所有的检测策略,我们也可以根据应用的具体需求只开启某些策略

webp

BlockCanary


         BlockCanary是一个非侵入式的性能监控函数库,它的用法和LeakCanary类似,只不过后者监控应用的内存泄漏,而BlockCanary主要用来监控应用主线程的卡顿,它的原理是利用主线程的消息队列处理机制,通过对比消息分发开始和结束的时间点来判断是否超过设定的时间,如果是,则判断为主线程卡顿,它的集成很简单,首先在build.gradle中添加在线依赖

webp

然后在Allpication类中进行配置和初始化即可

webp



作者:BlingBest
链接:https://www.jianshu.com/p/eb6826c40e05


打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP