附 Google 官网说明链接:Keeping your app responsive
ANR 一般有三种类型:
在 frameworks/base/ 下的 ActivityManagerService.java 中定义了具体的超时时间,默认为 5 秒
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
超时时间的计数一般是从按键分发给 APP 开始。超时的原因一般有两种:
需要满足的条件:
下面总结了几点常见的引起 ANR 的情况:
1. 应用程序自身引起的
例如,主线程阻塞(block)、挂起、死锁、死循环、执行耗时操作等
2. 其他应用进程引起的
例如,其他进程的 CPU 占用率过高、某一时刻系统 CPU 负载过高等,
都会导致当前进程无法抢到 CPU 时间片
3. IO wait
文件读写耗时较久,占用 CPU 时间片较长,导致 ANR
小技巧:如果你在解决其他问题时也需要查看 Java 进程中各个线程的函数堆栈信息,就可以使用向目标进程发送 SIGNAL_QUIT(3),它并不会让进程真正退出。
在系统输入管理服务进程(InputManagerService)中有一个线程(InputDispathcerThread)专门管理输入事件的分发,在该线程处理输入事件的过程中,回调 InputDispatcher 对象方法不断的检测处理过程是否超时,一旦超时,则会回调调用 InputMethod 对象的 notifyANR 方法,其会最终出发 AMS 中 handler 对象的 SHOW_NOT_RESPONDING_MSG 这个事件,显示 ANR 对话框。
广播分为三类:普通、有序、异步。只有有序(ordered)的广播才会发生超时,而在 AndroidManifest 中注册的广播都会被当做有序广播来处理,会被放在广播的队列中串行处理。AMS 在处理广播队列时,会设置一个超时时间,当处理一个广播达到超时时间的限制时,就会触发 BroadcastQueue 类对象的 processNextBroadcast 方法来判断是否超时,如果超时,就会终止该广播,触发 ANR 对话框。
…
系统发生 ANR 的时候,一般会以以下三种方式记录
-> 举例说明:
04-01 13:12:11.572 I/InputDispatcher( 220): Application is not responding:Window{2b263310com.android.email/com.android.email.activity.SplitScreenActivitypaused=false}.
5009.8ms since event, 5009.5ms since waitstarted
04-0113:12:11.572 I/WindowManager( 220): Input event
dispatching timedout sending
tocom.android.email/com.android.email.activity.SplitScreenActivity
// 1. 发生 ANR 的时间和生成 trace.txt 的时间
04-01 13:12:14.123 I/Process( 220): Sending signal. PID: 21404 SIG:
04-01 13:12:14.123 I/dalvikvm(21404):threadid=4: reacting to
signal 3
// 2. ANR 发生的地点
04-0113:12:15.872 E/ActivityManager( 220): ANR in
com.android.email(com.android.email/.activity.SplitScreenActivity)
04-0113:12:15.872 E/ActivityManager( 220):
// 3. ANR 的类型
Reason:keyDispatchingTimedOut
// 4. ANR 发生前 CPU 的负载情况
04-0113:12:15.872 E/ActivityManager( 220): Load: 8.68 / 8.37 / 8.53
04-0113:12:15.872 E/ActivityManager( 220): CPUusage from 4361ms to 699ms ago
04-0113:12:15.872 E/ActivityManager( 220): 5.5%21404/com.android.email: 1.3% user + 4.1% kernel / faults:
10 minor
04-0113:12:15.872 E/ActivityManager( 220): 4.3%220/system_server: 2.7% user + 1.5% kernel / faults: 11
minor 2 major
04-0113:12:15.872 E/ActivityManager( 220): 0.9%52/spi_qsd.0: 0% user + 0.9% kernel
04-0113:12:15.872 E/ActivityManager( 220): 0.5%65/irq/170-cyttsp-: 0% user + 0.5% kernel
04-0113:12:15.872 E/ActivityManager( 220): 0.5%296/com.android.systemui: 0.5% user + 0% kernel
04-0113:12:15.872 E/ActivityManager( 220): 100%TOTAL: 4.8% user + 7.6% kernel + 87% iowait
// 5. ANR 发生后 CPU 的负载情况
04-0113:12:15.872 E/ActivityManager( 220): CPUusage from 3697ms to 4223ms
04-0113:12:15.872 E/ActivityManager( 220): 25%21404/com.android.email: 25% user + 0% kernel / faults: 191 minor
04-0113:12:15.872 E/ActivityManager( 220): 16% 21603/__eas(par.hakan: 16% user + 0% kernel
04-0113:12:15.872 E/ActivityManager( 220): 7.2% 21406/GC: 7.2% user + 0% kernel
04-0113:12:15.872 E/ActivityManager( 220): 1.8% 21409/Compiler: 1.8% user + 0% kernel
04-0113:12:15.872 E/ActivityManager( 220): 5.5%220/system_server: 0% user + 5.5% kernel / faults: 1 minor
04-0113:12:15.872 E/ActivityManager( 220): 5.5% 263/InputDispatcher: 0% user + 5.5% kernel
04-0113:12:15.872 E/ActivityManager( 220): 32%TOTAL: 28% user + 3.7% kernel
从 LOG 上可以提取到的有效信息:
类型为 keyDispatchingTimedOut,即按键事件输入响应超时,发生在 com.android.email 应用中
从上面的 LOG 可以看出,total CPU 占用率达到 100%,并且 io wait 占用 87%,说明当前主线程正在进行 io 操作。
trace 文件中会标明 Thread 的状态,如:
DALVIK THREADS (12):
// 当前线程名 - "main"
// 线程优先级 - "prio=5"
// 线程id - "tid=1"
// 线程状态 - "Sleeping"
"main" prio=5 tid=1 Runnable
其他常用的状态定义在 Thread.java 文件中:
ThreadState (defined at "dalvik/vm/thread.h")
THREAD_UNDEFINED = -1, / makes enum compatible with int32_t /
THREAD_ZOMBIE = 0, / 线程死亡,终止运行 /
THREAD_RUNNING = 1, / 线程可运行或正在运行 /
THREAD_TIMED_WAIT = 2, / 执行了带有超时参数的 wait、sleep 或 join 函数 /
THREAD_MONITOR = 3, / 线程阻塞 /
THREAD_WAIT = 4, / 执行了无超时参数的 wait 函数 /
THREAD_INITIALIZING= 5, / 新建,正在初始化,为其分配资源 /
THREAD_STARTING = 6, / 新建,正在启动 /
THREAD_NATIVE = 7, / 正在执行 JNI 本地函数 /
THREAD_VMWAIT = 8, / 正在等待 VM 资源 /
THREAD_SUSPENDED = 9, / suspended, usually by GC or debugger /
举例分析:
----- pid 609 at 2019-02-16 05:00:10 -----
Cmd line: com.android.systemui
...
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x748ef1f0 self=0xb0851000
| sysTid=609 nice=0 cgrp=default sched=0/0 handle=0xb4a3f494
| state=S schedstat=( 392030973719 182772499142 1025298 ) utm=33261 stm=5941 core=2 HZ=100
| stack=0xbe5fa000-0xbe5fc000 stackSize=8MB
| held mutexes=
kernel: (couldn't read /proc/self/task/609/stack)
native: #00 pc 00019d74 /system/lib/libc.so (syscall+28)
native: #01 pc 0001d11d /system/lib/libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+88)
native: #02 pc 00063a99 /system/lib/libc.so (pthread_cond_wait+32)
native: #03 pc 0037cb2b /system/lib/libhwui.so (android::uirenderer::renderthread::DrawFrameTask::postAndWait()+174)
native: #04 pc 0037ca59 /system/lib/libhwui.so (android::uirenderer::renderthread::DrawFrameTask::drawFrame()+24)
at android.view.ThreadedRenderer.nSyncAndDrawFrame(Native method)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:823)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:3316)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3120)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2489)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1465)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7188)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
at android.view.Choreographer.doCallbacks(Choreographer.java:761)
at android.view.Choreographer.doFrame(Choreographer.java:696)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
以上的 trace 文件来自 systemui ANR,可以看出在 main 线程执行 NATIVE 方法的过程中,nSyncAndDrawFrame GPU 渲染的过程发生超时。