前言
ANR(Application Not Responding)即应用无响应,在Android系统中,应用发生的ANR由以下几种类型:
类型 | 说明 |
---|---|
KeyDispatchTimeout | 最常见的ANR类型是对输入事件5秒内无响应,比如按键或者触摸事件在此时间内无响应。 |
BroadcastTimeout | BroadcastTimeout是在指定时间内(原生系统默认是10s)内无法处理完成,并且没有结束执行onReceive。 |
ServiceTimeout | 这种类型在Android应用中出现的概率很小,是指Service在特定的时间(原生系统是20s)内无法处理完成。 |
引起ANR的根本原因总体来说有以下两种:
- 应用程序本身逻辑有缺陷,或者在某些异常场景触发了此缺陷,如主线程堵塞、死锁循环等导致的。
- 由于Android设备其他进程的CPU占用搞,导致当前应用进程无法抢占到CPU时间片。
主线程ANR的情况
- 耗时网络操作。
- 当有大量数据读写操作时再请求数据读写。
- 数据库操作(比如其他大数据量应用访问数据库导致数据库负载过重时)。
- 硬件操作(比如Camera)。
- 调用
thread_join()
/Sleep()
/Wait()
或者等待locker
的时候。 Service binder
数量达到上限。- Service忙导致超时无响应。
非主线程ANR的情况
- 非主线程持有lock,导致主线程等待lock超时。
- 非主线程终止或者崩溃导致主线程一直等待。
ANR文件介绍
在Android系统中,如果发生ANR,Logcat会产生对应的日志和一个trace文件,分析ANR的原因主要是分析这两个信息。trace文件在Android中的路径为/data/anr/traces.txt
,可以使用adb来获取,adb pull /data/anr/traces.txt
。
Logcat文件信息信息:
信息 | 说明 |
---|---|
ANR IN | 发生AND的具体类。 |
PID | 发生ANR的进程,系统在此时会产生trace文件,当前的时间点也是发生ANR的具体时间,以及生成trace文件的时间。 |
Reason | 当前ANR的类型以及导致ANR的原因。 |
CPU usage | CPU的使用情况。 |
从Logcat中除了能看出在哪个类发生了ANR以及ANR的类型,具体的原因主要还是要看CPU的使用情况,如果CPU使用量很少,说明主线程可能堵塞,如果IOwait很高,说明ANR有可能是由于主线程进行耗时I/O操作造成的。
ANR文件分析
使用如下代码,模拟ANR:
public void onANR(View view) {
Toast.makeText(this, "开始ANR模拟", Toast.LENGTH_SHORT).show();
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
模拟结果如下:(注意:时间有点长,请耐心等待O(∩_∩)O)
使用adb pull /data/anr/
提取发生ANR之后生成的traces文件。
使用Android Studio上提供的一个分析traces文件的工具:Analyze Stacktrace
。Analyze Stacktrace可以更加直观地分析导致ANR的工具。
Analyze Stacktrace使用方法:
- 在Android Studio的工具栏中,选择
Analyze->Analyze Stackstrace
,打开Analyze Stackstrace
工具窗口。 - 将traces.txt中的内容复制到窗口,单击Normalize按钮,生成Thread Dump列表,左边为所有线程列表,右边为选中线程的具体信息。
导致AND的信息为:
"main" prio=5 tid=1 Sleeping | group="main" sCount=1 dsCount=0 obj=0x74239fa8 self=0x7f3e24897000 | sysTid=29044 nice=0 cgrp=apps sched=0/0 handle=0x7f3e27ba86a0 | state=S schedstat=( 0 0 0 ) utm=48 stm=43 core=0 HZ=100 | stack=0x7fff22dd1000-0x7fff22dd3000 stackSize=8MB | held mutexes=
at java.lang.Thread.sleep!(Native method)
- sleeping on <0x1b893c4d> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:1031)
- locked <0x1b893c4d> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:985)
at link_work.myapplication.MainActivity.onANR(MainActivity.java:147)
at java.lang.reflect.Method.invoke!(Native method)
at java.lang.reflect.Method.invoke(Method.java:372)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:4756)
at android.view.View$PerformClick.run(View.java:19749)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke!(Native method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
从上面的信息中at link_work.myapplication.MainActivity.onANR(MainActivity.java:147)
可以看出问题发生在这里。主要原因是因为线程休眠的时间太长了,导致了ANR。
ANR监控
卡顿监控可以通过一个子线程向主线程发消息,通过时间差来判断是否发生卡顿。
ANR监控工具
引起ANR情况分析
引起ANR的情况有很多中,下面逐一分析。
Input系统—ANR
说明 | Log提示 |
---|---|
无窗口, 有应用 | Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up. |
窗口暂停 | Waiting because the [targetType] window is paused. |
窗口未连接 | Waiting because the [targetType] window’s input channel is not registered with the input dispatcher. The windo |