android 开发过程中有时会遇见项目无响应异常,这类异常异常并不像Crash那样打印log信息,因此无法被捕获。所以来看一下这个ANR异常的出现原因及其定位。
主线程阻塞:网络访问等的线程阻塞,CPU满负荷, I/O阻塞,内存不够用等都会造成代码长时间执行,后面统称为 主线程阻塞。
在一般的理解中ANR就是将主线程阻塞,当主线程阻塞超过相应阀值则会触发ANR异常。OK我们来写一个栗子测试一下。
运行之后发现并没有出现ANR异常,程序乖乖的延时5秒后再执行后面的代码。将延时修改为15秒之后结果还是这样。
其实在onCreate方法中来执行延时函数是不会触发无响应异常的。这里就需要来看一下ANR的介绍了。
简单来说:ANR就是当前的事件没有得到及时有效的处理。
也就是说,ANR的触发是跟事件处理有关系的,例如当系统接收到点击事件之后就会将其传递给相应的应用,正常情况下事件会传递到处理这个点击事件的view来做具体逻辑处理。但是如果这个时候主线程是阻塞状态的,而系统又不允许其他线程去处理UI相关的事件,这时事件就会被阻塞住而无法及时处理。当阻塞时间超过一定阀值就会触发这个异常。
我们把界面改成两个按钮,并在第一个按钮的点击事件里来进行延时阻塞线程。
运行起来之后先只点击第一个进行延时然后不做任何操作。看到程序可以正常的运行完而没有异常。但是当点击完第一个按钮来阻塞住UI线程之后点击第二个按钮,则会出现ANR异常。并且catch里并没有捕获这个异常。
结论:主线程在无事件处理时本身就会处于阻塞状态,当有点击事件传递给主线程或者其他线程使用Handler向MessageQueue中存放消息,导致loop被唤醒时,主线程就会被唤醒执行相应事件,但是这时如果主线程还是被阻塞住,就会导致事件无法处理。当时间超过阀值(activity貌似是5s)时,就会触发ANR异常。
2.1
在发生anr时log日志只会打印
I/art: Thread[2,tid=25238,WaitingInMainSignalCatcherLoop,Thread*=0xafa0e400,peer=0x12c2a080,"Signal Catcher"]: reacting to signal 3
I/art: Wrote stack traces to '/data/anr/traces.txt'
这两个I类的log信息,所以定位ANR异常不能只看异常日志
首先打开studio 点击Tools --> Android --> Android Device Monltor 打开DDMS界面
然后选中要调试的进程之后刷新线程
刷新完成之后就会出现当前运行的线程,第一个name为main 则为主线程我们主要就是看这个线程
选中这个线程底部有Refresh按钮,之后在程序阻塞时刷新该线程就可以显示其阻塞位置。
在现有例子下,我们点击第一个按钮,先让线程阻塞,然后点击Refresh。
这里可以就可以看到主程序阻塞的位置了。在TestActivity中的onClick方法中,在执行第41行代码时阻塞。
至此,ANR异常解析及定位大功告成。
最后在CPU使用率过高及内存不够时都可能造成ANR异常,具体可参看Android App优化之ANR详解。