什么是ANR
ANR全称Application Not Responding
,意思就是程序未响应。如果一个应用无法响应用户的输入,系统就会弹出一个ANR对话框,如下图所示,用户可以自行选择继续等待亦或者是停止当前程序。
出现场景
主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞。 主线程中存在耗时的计算 主线程中错误的操作,比如Thread.wait或者Thread.sleep等
Android系统会监控程序的响应状况,一旦出现下面两种情况,则弹出ANR对话框
应用在5秒 内未响应用户的输入事件(如按键或者触摸) BroadcastReceiver未在10秒 内完成相关的处理
如何避免
基本的思路就是将IO操作在工作线程来处理,减少其他耗时操作和错误操作
使用AsyncTask 处理耗时IO操作。 使用Thread或者HandlerThread时,调用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同。 使用Handler 处理工作线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程。 Activity的onCreate和onResume回调中尽量避免耗时的代码 BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理。
画龙点睛
通常100到200毫秒就会让人察觉程序反应慢,为了更加提升响应,可以使用下面的几种方法
如果程序正在后台处理用户的输入,建议使用让用户得知进度,比如使用ProgressBar控件。 程序启动时可以选择加上欢迎界面,避免让用户察觉卡顿。 使用Systrace和TraceView找出影响响应的问题。
如何定位
如果开发机器上出现问题,我们可以通过查看/data/anr/traces.txt
即可,最新的ANR信息在最开始部分。我们从stacktrace中即可找到出问题的具体行数。本例中问题出现在MainActivity.java 27行,因为这里调用了Thread.sleep方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root @htc_m8tl :/ # cat / data / anr / traces . txt | more
----- pid 30307 at 2015 - 05 - 30 14 : 51 : 14 -----
Cmd line: com . example . androidyue . bitmapdemo
JNI: CheckJNI is off ; workarounds are off ; pins = 0 ; globals = 272
DALVIK THREADS:
( mutexes: tll = 0 tsl = 0 tscl = 0 ghl = 0 )
"main" prio = 5 tid = 1 TIMED_WAIT
| group = "main" sCount = 1 dsCount = 0 obj = 0x416eaf18 self = 0x416d8650
| sysTid = 30307 nice = 0 sched = 0 / 0 cgrp = apps handle = 1074565528
| state = S schedstat =( 0 0 0 ) utm = 5 stm = 4 core = 3
at java . lang . VMThread . sleep ( Native Method )
at java . lang . Thread . sleep ( Thread . java : 1044 )
at java . lang . Thread . sleep ( Thread . java : 1026 )
at com . example . androidyue . bitmapdemo . MainActivity $1 . run ( MainActivity . java : 27 )
at android . app . Activity . runOnUiThread ( Activity . java : 4794 )
at com . example . androidyue . bitmapdemo . MainActivity . onResume ( MainActivity . java : 33 )
at android . app . Instrumentation . callActivityOnResume ( Instrumentation . java : 1282 )
at android . app . Activity . performResume ( Activity . java : 5405 )
如果是线上版本引起的,Google Play后台有相关的数据可以帮助查看分析并解决问题。
细致分析
提问 : BroadcastReceiver过了60秒居然没有ANR? 现场代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class NetworkReceiver extends BroadcastReceiver {
private static final String LOGTAG = "NetworkReceiver" ;
@Override
public void onReceive ( Context context , Intent intent ) {
Log . i ( LOGTAG , "onReceive intent=" + intent );
try {
Thread . sleep ( 60000 );
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
Log . i ( LOGTAG , "onReceive end" );
}
}
回答 :实际上已经发生了ANR,只是没有进行对话框弹出而已。这种ANR就是background ANR,即后台程序的ANR,我们可以通过过滤日志验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
adb logcat | grep "NetworkReceiver|ActivityManager|WindowManager"
I / NetworkReceiver ( 4109 ): onReceive intent = Intent { act = android . net . conn . CONNECTIVITY_CHANGE flg = 0x8000010 cmp = com . example . androidyue . bitmapdemo /. NetworkReceiver ( has extras ) }
I / ActivityManager ( 462 ): No longer want com . android . exchange ( pid 1054 ): empty # 17
I / NetworkReceiver ( 4109 ): onReceive end
W / BroadcastQueue ( 462 ): Receiver during timeout: ResolveInfo { 5342 dde4 com . example . androidyue . bitmapdemo . NetworkReceiver p = 0 o = 0 m = 0x108000 }
E / ActivityManager ( 462 ): ANR in com . example . androidyue . bitmapdemo
E / ActivityManager ( 462 ): Reason: Broadcast of Intent { act = android . net . conn . CONNECTIVITY_CHANGE flg = 0x8000010 cmp = com . example . androidyue . bitmapdemo /. NetworkReceiver ( has extras ) }
E / ActivityManager ( 462 ): Load: 0.37 / 0.2 / 0.14
E / ActivityManager ( 462 ): CPU usage from 26047 ms to 0 ms ago:
E / ActivityManager ( 462 ): 0.4 % 58 / adbd: 0 % user + 0.4 % kernel / faults: 1501 minor
E / ActivityManager ( 462 ): 0.3 % 462 / system_server: 0.1 % user + 0.1 % kernel
E / ActivityManager ( 462 ): 0 % 4109 / com . example . androidyue . bitmapdemo : 0 % user + 0 % kernel / faults: 6 minor
E / ActivityManager ( 462 ): 1.5 % TOTAL: 0.5 % user + 0.9 % kernel + 0 % softirq
E / ActivityManager ( 462 ): CPU usage from 87 ms to 589 ms later:
E / ActivityManager ( 462 ): 1.8 % 58 / adbd: 0 % user + 1.8 % kernel / faults: 30 minor
E / ActivityManager ( 462 ): 1.8 % 58 / adbd: 0 % user + 1.8 % kernel
E / ActivityManager ( 462 ): 4 % TOTAL: 0 % user + 4 % kernel
W / ActivityManager ( 462 ): Killing ProcessRecord { 5326 d418 4109 : com . example . androidyue . bitmapdemo / u0a10063 }: background ANR
I / ActivityManager ( 462 ): Process com . example . androidyue . bitmapdemo ( pid 4109 ) has died .
除了日志,我们还可以根据前面提到的查看traces.txt文件。
提问 :可以更容易了解background ANR么?
回答 :当然可以,在Android开发者选项—>高级—>显示所有”应用程序无响应“勾选即可对后台ANR也进行弹窗显示,方便查看了解程序运行情况。