ANR全称是Application Not Responding,意思是应用程序无响应。相信从事Android开发的肯定遇到过。ANR的直观体验是用户在操作App的过程中,感觉界面卡顿,当界面卡顿超过一定时间(一般5秒),就会出现ANR对话框。ANR对于一个应用来说是不能承受之痛,其影响并不比应用发生Crash小。
ANR产生的原因
只有当应用程序的UI线程响应超时才会引起ANR,超时产生原因一般有两种。
- 当前的事件没有机会得到处理,例如UI线程正在响应另一个事件,当前事件由于某种原因被阻塞了。
- 当前的事件正在处理,但是由于耗时太长没能及时完成。
根据ANR产生的原因不同,超时事件也不尽相同,从本质上将,产生ANR的原因有三种,大致可以对应到Android中四大组件中的三个(Activity/View,BroadcastReceiver和Service)。
KeyDispatchTimeout
最常见的一种类型,原因就是View的点击事件或者触摸事件在特定的时间(5s)内无法得到响应。
BroadcastTimeout
原因是BroadcastReceiver的onReceive()函数运行在主线程中,在特定的时间(10s)内无法完成处理。
ServiceTimeout
比较少出现的一种类型,原因是Service的各个生命周期函数在特定时间(20s)内无法完成处理。
典型的ANR问题场景
- 应用程序UI线程存在耗时操作。例如在UI线程中进行联网请求,数据库操作或者文件操作等。
- 应用程序的UI线程等待子线程释放某个锁,从而无法处理用户的输入。
- 耗时的动画需要大量的计算工作,可能导致CPU负载过重。
ANR的定位和分析
当发生ANR时,可以通过结合Logcat日志和生成的位于手机内部存储的/data/anr/traces.tex文件进行分析和定位。
Cmd line: com.anly.githubapp // 最新的ANR发生的进程(包名)
...
DALVIK THREADS (41):
"main" prio=5 tid=1 Sleeping
| group="main" sCount=1 dsCount=0 obj=0x73467fa8 self=0x7fbf66c95000
| sysTid=2976 nice=0 cgrp=default sched=0/0 handle=0x7fbf6a8953e0
| state=S schedstat=( 0 0 0 ) utm=60 stm=37 core=1 HZ=100
| stack=0x7ffff4ffd000-0x7ffff4fff000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep!(Native method)
- sleeping on <0x35fc9e33> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:1031)
- locked <0x35fc9e33> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:985) // 主线程中sleep过长时间, 阻塞导致无响应.
at com.tencent.bugly.crashreport.crash.c.l(BUGLY:258)
- locked <@addr=0x12dadc70> (a com.tencent.bugly.crashreport.crash.c)
at com.tencent.bugly