一、ANR简介
ANR:全称是Application Not Respond,就是应用程序无响应,这也是经常会发生的一种问题,任何一个应用程序都有主线程,在Android中,因为UI的操作都在主线程中完成,所以主线程也被成为UI线程,UI线程中会做一些和用户交互相关性非常大的工作,所以UI线程不能有明显的卡顿,一般我们不建议在UI线程中执行较耗时的操作。如果一些粗心的开发者在UI线程中做了一些耗时的操作,会有非常明显的卡顿,严重的情况下还会产生ANR。
二、ANR产生机制
硬件收集到的输入事件都会插入到WindowManagerService.mQueue(KeyQ类)中,同时 WindowManagerService.InputDispatcherThread这个线程运行在系统进程中,它循环地处理 mQueue内的消息。InputDispatcherThread在处理事件的过程中,会不断的检测处理过程是否超时,一旦超时,会通过一 系列的回调通知AMS,由AMS控制弹出ANR对话框。
超时检测处理过程实际上是一个wait()/notify()的过程。首先,如果一个输入事件进入检测流程时,发现当前正在处理输入事件,就会调用 wait()阻塞输入事件处理线程,wait的时间为10s。在一个输入事件处理完成时,会调用notify()让输入事件处理线程继续执 行。继续执行时就会计算整个过程一共花了多长时间来判断是否是ANR。
三、流程解析
3.1 ContentProvider处理的时候发生超时
ContentProviderClient中提供了setDetectNotResponding(…)函数来帮助监听ContentProvider的超时情况,不过一般情况下都没有ContentProvider会这么设置,反正Android在这里提供给开发者一种思路,ContentProvider是提供超时保护的。
3.2 InputDispatcher发生超时
但是正常情况下还是第2种发生的次数比较多,第1种目前发生的次数比较少,其实原理都是都是一样的。ContentProvider处理的时候发生超时,就是本地操作本地的content provider发生延时。本文主要从InputDispatcher发生超时讲解。
这要从本地的inputFlinger讲起,Android系统处理input时间的就是inputFlinger,每次处理input 事件的时候,要执行到native的一个方法:
frameworks/native/services/inputflinger/InputDispatcher.cpp中的findFocusedWindowTargetsLocked(…)方法,每次处理input的事件的时候都会走到这个函数中。此函数中在开始的时候有一段关键的判断代码:
if (mFocusedWindowHandle == NULL) {
if (mFocusedApplicationHandle != NULL) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplicationHandle, NULL, nextWakeupTime,
"Waiting because no window has focus but there is a "
"focused application that may eventually add a window "
"when it finishes starting up.");
goto Unresponsive;
}
ALOGI("Dropping event because there is no focused window or focused application.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
如果当前没有焦点窗口且没有焦点应用程序,然后放弃事件。继续往下看,执行的handleTargetsNotReadyLocked(…)函数:
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime, const char* reason) {
//......
nsecs_t timeout;
if (windowHandle != NULL) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else if (applicationHandle != NULL) {
timeout = applicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else {
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}
//......
if (currentTime >= mInputTargetWaitTimeoutTime) {
onANRLocked(currentTime, applicationHandle, windowHandle,
entry->eventTime, mInputTargetWaitStartTime, reason);
// Force poll loop to wake up immediately on next iteration once we get the
// ANR response back from the policy.
*nextWakeupTime = LONG_LONG_MIN;