什么是ANR
ANR是Android中一个比较有代表性的异常,它的英文全称是Application Not Responding,就是应用无响应的意思。Android开发中如果主线程(UI线程)在规定的时间内没有处理完相应的工作就会发生ANR。ANR的直观感受就是操作应用的过程中明显的卡顿,主要的原因是在主线程中进行了太多的耗时操作。
ANR产生的原因
当应用的主线程的响应超时才会引发ANR,那么产生响应超时的原因可分为两种,第一种是当前事件没有机会得到处理,第二种是当前事件正在处理,但由于耗时太长没能及时完成任务。
ANR出现的几种情况
本质上讲产生ANR的原因分三种,对应着Activity/View、BroadcastReceiver、Service;
keyDispatchTimeOut
这是最典型也是最常见的一种类型,原因就是View的点击事件或触摸事件在特定的时间(5s)内没有得到响应;
BroadcastTimeOut
BroadcastReceiver的onReceiver()函数运行在主线程中,在特定的时间(10s)内没有完成处理;
ServiceTimeOut
比较少出现的一种类型,Service的各个生命周期函数在特定时间(20s)内无法得到处理引起;
造成ANR问题的原因(产生场景)
1.主线程在做一些耗时操作,例如在主线程中进行网络请求、数据库操作或者文件操作可能会导致UI线程无法及时处理用户的输入信息;
2.主线程被其他线程锁。应用程序在UI线程等待子线程释放某个锁,导致无法处理用户的输入;;
3.CPU被其他进程占用,该进程没被分配到足够的cpu资源。例如耗时的动画需要大量的计算工作导致CPU负载过重;;
发生ANR后判断产生原因很关键,到底是因为主线程进行了耗时操作、主线程被其他线程锁还是CPU被其他进程占用,该进程没被分配到足够的CPU资源。我们需要从这些角度判断引起异常的原因然后针对性处理。
ANR的定位和分析
ANR定位
应用发生ANR的时候系统会收集ANR相关的信息内容提供给开发者用来处理问题。
在Logcat中有ANR的相关信息,收集ANR的CPU的使用情况还有trace信息,也就是当前各个线程执行情况。
trace文件保存在Android应用目录/data/anr/traces.txt中,ANR进程打印出的Log信息也有一定的参考价值。
ANR分析
1.从log中找到ANR发生的信息;
2.在该条log之后会有CPU usage的信息,表明了CPU在ANR前后的用量,从各种CPU Usage信息中分析;
(1)如果某些进程的CPU占用百分比较高,发生ANR的进程CPU占用为0%或非常低,则认为CPU资源被占用,进程没有被分配足够的资源,从而发生了ANR。这种情况多数可以认为是系统状态的问题,并不是由本应用造成。
(2)如果发生ANR的进程CPU占用比较高,很大可能是因为应用内一些代码不合理消耗掉了CPU资源,例如出现了死循环或者后台有许多线程执行任务等原因。
(3)如果CPU总用量不高,该进程和其他进程的占用过高,这有一定概率是由于某些主线程的操作就是耗时过长,或者由于主进程被锁造成的。
3.确定问题需要进一步分析trace文件,该文件记录了发生ANR前后该进程的各个线程的stack,特别需要注意的是主线程的stack;
(1)主线程是running或者native而对应的栈对应了我们应用中的函数,则很有可能就是执行该函数的时候发生了超时;
(2)主线程被block非常明显的线程被锁,这时候可以看是被哪个线程锁了,考虑优化代码。如果是死锁问题就需要及时解决。
(3)由于抓trace的时刻有可能耗时操作已经执行完了,这时候trace就没什么用了,这种情况很可能是由于进程的其他线程消耗掉了CPU的资源,这就需要分析其他线程的trace以及ANR前后该进程自己输出的log了。
降低ANR产生概率
1.不在主线程(UI线程)进行耗时操作;
2.不在BroadcastReceiver的onReceiver()方法中进行耗时操作;
3.各个组件的声明周期函数都不应该有太耗时的操作;
4.尽量避免主线程被锁的情况,在一些同步的操作主线程有可能被锁,需要等待其他线程释放响应锁才能继续执行,这样会一定的ANR风险,对于这种情况有时也可以用异步线程来执行响应的逻辑。另外要避免死锁的发生