最近在项目上处理比较多的应用No Focused Window类型的问题,于是把流程梳理一遍(基于Android R),了解问题发生的原因才好有相应的应对措施。
首先看No Focused Window是什么样的表现,通常是应用ANR时的reason是:
Input dispatching timed out sending to application ActivityRecord{xxx}. ActivityRecord{xxx} does not have a focused window
还有一种类型如下:
Appilcation is not responding. waited 5000ms for ActivityRecord{xxx}
本文重点讲xxx does not have a focused window
的流程,也会提到什么情况下会出现waited 5000ms for xxx
。
首先要知道的是,这两个类型的ANR都是InputDispatcher
触发的,所以要从input事件分发的流程去看触发ANR的过程。当有input事件过来的时候,就会触发到InputDispatcher
的dispatchOnce()
,相关代码如下,关键流程有相关注释。
// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all();
// 判断mCommandQueue是否为空
// mCommandQueue是通过postCommandLocked()添加元素
// postCommandLocked()在多处地方调用,总结来说就是有事件触发时mCommandQueue不会为空
// onFocusChangedLocked() -> postCommandLocked()
// onAnrLocked() -> postCommandLocked()
// dispatchConfigurationChangedLocked() -> postCommandLocked()
// dispatchKeyLocked() -> postCommandLocked()
if (!haveCommandsLocked()) {
// 一般来说会走到dispatchOnceInnerLocked流程
dispatchOnceInnerLocked(&nextWakeupTime);
}
// 执行mCommandQueue中的命令,如果mCommandQueue为空返回false,有命令则为true
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
const nsecs_t nextAnrCheck = processAnrsLocked();
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LONG_LONG_MAX) {
mDispatcherEnteredIdle.notify_all();
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
一般来说会走到dispatchOnceInnerLocked()
这个流程,相关代码如下,关键流程有注释。
// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
// ......
// mPendingEvent默认为空,会从mInboundQueue中获取到值
if (!mPendingEvent) {
// mInboundQueue是通过enqueueInboundEventLocked()赋值,所以有事件的时候mInboundQueue不为空
// notifyConfigurationChanged() -> enqueueInboundEventLocked()
// notifyKey() -> enqueueInboundEventLocked()
// notifyMotion() -> enqueueInboundEventLocked()
// notifyDeviceReset() -> enqueueInboundEventLocked()
// injectInputEvent() -> enqueueInboundEventLocked()
// setInputWindowsLocked() -> enqueueFocusEventLocked()
if (mInboundQueue.empty()) {
// ......
// 如果mInboundQueue也没有pending的事件,直接返回
if (!mPendingEvent) {
return;
}
} else {
// 一般走这个流程,取mInboundQueue第一个元素
mPendingEvent = mInboundQueue.front();
mInboundQueue.pop_front();
traceInboundQueueLengthLocked();
}
// ......
}
// 走到这个地方,mPendingEvent一般不会为空
ALOG_ASSERT(mPendingEvent != nullptr);
bool done = false;
DropReason dropReason = DropReason::NOT_DROPPED;
// ......
// 根据pendingEvent的类型创建不同的实例去分发事件
// 不同的入口的类型不一样,如调用了notifyKey()就会创建一个KeyEntry
switch (mPendingEvent->type) {
// 来源:notifyConfigurationChanged() -> new ConfigurationChangedEntry()
case EventEntry::Type::CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
static_cast<ConfigurationChangedEntry*>(mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
break;
}
// 来源:notifyDeviceReset() -> new DeviceResetEntry()
case EventEntry::Type::DEVICE_RESET: {
DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
done = dispatchDeviceResetLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
break;
}
// 来源:setInputWindowsLocked() -> enqueueFocusEventLocked() -> new FocusEntry()
case EventEntry::Type::FOCUS: {
FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
dispatchFocusLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
break;
}
// 来源:notifyKey() -> new KeyEntry()
case EventEntry::Type::KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
// ......
// 跳转到findFocusedWindowTargetsLocked() -> dispatchEventLocked()
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
// 来源:notifyMotion() -> new MotionEntry()
case EventEntry::Type::MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
// ......
// 跳转到findFocusedWindowTargetsLocked() -> dispatchEventLocked()
done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
}
// ......
}
前面处理按键Key事件(dispatchKeyLocked
)或者触摸Motion事件(dispatchMotionLocked
)都是需要找到焦点窗口去处理,都会调用到findFocusedWindowTargetsLocked()
,findFocusedWindowTargetsLocked()
流程如下,关键步骤有注释。
// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry& entry,
std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime) {
std::string reason;
int32_t displayId = getTargetDisplayId(entry);
// mFocusedWindowHandlesByDisplay在setInputWindowsLocked()里赋值
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
// mFocusedApplicationHandlesByDisplay在setFocusedApplication()里赋值
sp<InputApplicationHandle> focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
// focusedWindowHandle和focusedApplicationHandle都为空时表示当前无窗口,该事件会被丢弃,不会执行dispatchEventLocked
// 一般出现两个都为空的场景,是在窗口切换的过程,此时不处理事件注入
if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
EventEntry::typeToString(entry.type), displayId);
return INPUT_EVENT_INJECTION_FAILED;
}
// focusedWindowHandle为空但focusedApplicationHandle不为空时开始ANR检查
if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
// 默认mNoFocusedWindowTimeoutTime没有值,第一次检查ANR会走下面这个流程
if (!mNoFocusedWindowTimeoutTime.has_value()) {
// DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s * HwTimeoutMultiplier();
// 默认input dispatch timeout时间时5s
const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
// 给mNoFocusedWindowTimeoutTime赋值,触发ANR时会检查这个值是否为空,不为空才触发ANR
mNoFocusedWindowTimeoutTime = currentTime + timeout;
// 把当前的focusedApplicationHandle赋值给mAwaitedFocusedApplication,触发ANR时会检查这个值是否为空,不为空才触发ANR
mAwaitedFocusedApplication = focusedApplicationHandle;
mAwaitedApplicationDisplayId = displayId;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
// 返回INPUT_EVENT_INJECTION_PENDING表示dispatchKeyLocked()或者dispatchMotionLocked()为false
return INPUT_EVENT_INJECTION_PENDING;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
ALOGE("Dropping %s event because there is no focused window",
EventEntry::typeToString(entry.type));
return INPUT_EVENT_INJECTION_FAILED;
} else {
// Still waiting for the focused window
return INPUT_EVENT_INJECTION_PENDING;
}
}
// 如果走到这个流程,说明没有ANR,清空mNoFocusedWindowTimeoutTime和mAwaitedFocusedApplication
resetNoFocusedWindowTimeoutLocked();
// ......
// Done.
return INPUT_EVENT_INJECTION_SUCCEEDED;
}
前面看到,是否触发ANR跟focusedWindowHandle
和focusedApplicationHandle
有关,那么这两个值的赋值流程是在哪里呢?
跟踪代码流程,可以看到,这两个流程都是WindowManagerService在窗口切换的过程中,设置到InputDispatcher中的,最后调用setInputWindowsLocked()
设置focusedWindowHandle
,调用setFocusedApplication()
设置focusedApplicationHandle
,相关流程如下,关键步骤有注释。
// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// 当VSYNC信号来了之后,会调用到SurfaceFlinger的onMessageInvalidate()方法
// SurfaceFlinger::onMessageInvalidate()
// ==> SurfaceFlinger: updateInputFlinger()
// ==> SurfaceFlinger: updateInputWindowInfo()
// ==> InputManager::setInputWindows()
// ==> InputDispatcher::setInputWindows()
// ==> InputDispatcher::setInputWindowsLocked()
void InputDispatcher::setInputWindowsLocked(
const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
// ......
const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
// 更新mWindowHandlesByDisplay这个map,然后通过getWindowHandlesLocked()找newFocusedWindowHandle
updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
bool foundHoveredWindow = false;
// 在mWindowHandlesByDisplay这个map里面找newFocusedWindowHandle
for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
// newFocusedWindowHandle要不为空,windowHandle具备focusable和visible属性
if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
windowHandle->getInfo()->visible) {
// 给newFocusedWindowHandle赋值,最后这个值存到mFocusedWindowHandlesByDisplay这个map
newFocusedWindowHandle = windowHandle;
}
if (windowHandle == mLastHoverWindowHandle) {
foundHoveredWindow = true;
}
}
if (!foundHoveredWindow) {
mLastHoverWindowHandle = nullptr;
}
// 在mFocusedWindowHandlesByDisplay这个map里找当前的焦点窗口
sp<InputWindowHandle> oldFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
// 判断oldFocusedWindowHandle是否等于newFocusedWindowHandle,如果相等则不走focus change流程
if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
// 如果当前的焦点窗口不为空,需要从mFocusedWindowHandlesByDisplay移除掉
if (oldFocusedWindowHandle != nullptr) {
sp<InputChannel> focusedInputChannel =
getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (focusedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
// 新建一个FocusEntry加入到mInboundQueue去dispatch
enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
}
// oldFocusedWindowHandle不为空时需要移除旧的
mFocusedWindowHandlesByDisplay.erase(displayId);
}
// 走到这个流程,如果oldFocusedWindowHandle不为空,newFocusedWindowHandle为空,那么在findFocusedWindowTargetsLocked()中的focusedWindowHandle为空
// 如果newFocusedWindowHandle不为空,更新mFocusedWindowHandlesByDisplay
if (newFocusedWindowHandle != nullptr) {
// 更新mFocusedWindowHandlesByDisplay,在findFocusedWindowTargetsLocked()时用到
mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
// 新建一个FocusEntry加入到mInboundQueue去dispatch
enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
}
if (mFocusedDisplayId == displayId) {
// 添加focusChanged到mCommandQueue,在dispatchOnce时会执行
onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
}
}
// ......
}
setFocusedApplication()
流程如下:
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
void InputDispatcher::setFocusedApplication(
int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
{ // acquire lock
std::scoped_lock _l(mLock);
// 获取当前的focusedApplicationHandle
sp<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
// 如果当前的focusedApplicationHandle跟触发ANR是的focusedApplicationHandle是一样且
// 新的focusedApplicationHandle跟旧的不一样,说明focusedApplicationHandle有更新
// 需要重置ANR计时
if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
inputApplicationHandle != oldFocusedApplicationHandle) {
// 重置ANR计时
resetNoFocusedWindowTimeoutLocked();
}
if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
if (oldFocusedApplicationHandle != inputApplicationHandle) {
// 赋值新的inputApplicationHandle到mFocusedApplicationHandlesByDisplay,在findFocusedWindowTargetsLocked()时用到
mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
} else if (oldFocusedApplicationHandle != nullptr) {
// 如果inputApplicationHandle为空,oldFocusedApplicationHandle不为空,需要清楚oldFocusedApplicationHandle
oldFocusedApplicationHandle.clear();
// 走到这个流程会出现findFocusedWindowTargetsLocked()中focusedApplicationHandle为空
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
在dispatchOnce()
处理完事件后,会走processAnrsLocked()
检查是否有ANR发生。相关流程如下,关键流程有注释。
// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
nsecs_t InputDispatcher::processAnrsLocked() {
const nsecs_t currentTime = now();
nsecs_t nextAnrCheck = LONG_LONG_MAX;
// 在findFocusedWindowTargetsLocked()中,如果focusedWindowHandle为空,focusedApplicationHandle不为空,以下条件就会满足
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
// mNoFocusedWindowTimeoutTime为检查时间+5s,如果currentTime大于等于mNoFocusedWindowTimeoutTime,表示超时
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
// 触发ANR流程,此处触发的ANR类型是xxx does not have a focused window
processNoFocusedWindowAnrLocked();
// 清空mAwaitedFocusedApplication,下次就不会再走ANR流程
mAwaitedFocusedApplication.clear();
mNoFocusedWindowTimeoutTime = std::nullopt;
return LONG_LONG_MIN;
} else {
// Keep waiting
const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
// 还没有超时,更新检查时间
nextAnrCheck = *mNoFocusedWindowTimeoutTime;
}
}
// 如果到这个流程,说明focusedWindowHandle不为空,或者是检查到了focusedWindowHandle为空,focusedApplicationHandle不为空但未超时
// nextAnrCheck此时可能为mNoFocusedWindowTimeoutTime,也可能是LONG_LONG_MAX
// mAnrTracker.firstTimeout()默认为max,在startDispatchCycleLocked()更新mAnrTracker.firstTimeout()
// mAnrTracker.firstTimeout为startDispatchCycleLocked()调用时间+5s
nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
// currentTime小于nextAnrCheck表示未超时,等待下一次检查
if (currentTime < nextAnrCheck) { // most likely scenario
return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck
}
// 如果走到这个流程,表示currentTime大于mNoFocusedWindowTimeoutTime或者是mAnrTracker.firstTimeout()
// 场景应该是在触发xxx does not have a focused window类型ANR前超过了mAnrTracker.firstTimeout()
sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
if (connection == nullptr) {
ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
return nextAnrCheck;
}
// 将responsive置为false,避免更新mAnrTracker
connection->responsive = false;
// 擦除当前token的ANR记录并触发ANR
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
// 如果走到这个流程,ANR类型是xxx is not responding. Waited xxx ms for xxx
// 这个地方,focusedWindowHandle和focusedApplicationHandle都是不为空的场景
onAnrLocked(*connection);
return LONG_LONG_MIN;
}
类型是xxx does not have a focused window
走processNoFocusedWindowAnrLocked()
流程,最后也是会走到onAnrLocked()
,最后执行doNotifyAnrLockedInterruptible()
。processNoFocusedWindowAnrLocked()
是google后续补上的拯救措施,一开始代码是不包含的,会导致ANR存在误判的场景。processNoFocusedWindowAnrLocked()
流程如下,关键流程有注释。
// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::processNoFocusedWindowAnrLocked() {
// 在触发ANR前,再获取一次当前的focusedApplication
sp<InputApplicationHandle> focusedApplication =
getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
// 检查触发ANR时的条件是focusedApplication不为空
// 如果此时focusedApplication为空,或者focusedApplication不等于前一个mAwaitedFocusedApplication表示已经切换application focus,取消触发ANR
if (focusedApplication == nullptr ||
focusedApplication->getApplicationToken() !=
mAwaitedFocusedApplication->getApplicationToken()) {
// Unexpected because we should have reset the ANR timer when focused application changed
ALOGE("Waited for a focused window, but focused application has already changed to %s",
focusedApplication->getName().c_str());
return; // The focused application has changed.
}
// 在触发ANR前,再获取一次当前的focusedWindowHandle
const sp<InputWindowHandle>& focusedWindowHandle =
getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
// 检查触发ANR时focusedWindowHandle为空,如果此时focusedWindowHandle不为空,取消触发ANR
if (focusedWindowHandle != nullptr) {
return; // We now have a focused window. No need for ANR.
}
// 通过前面的判断,还是无法拦截,说明该ANR无可避免,最终触发ANR
// 早期代码没有前面一系列的判断,是直接触发的ANR,会在性能较差的场景下出现误判
onAnrLocked(mAwaitedFocusedApplication);
}
onAnrLocked()
有两个实现,两个参数不一样,一个是传inputConnection,一个是传InputApplicationHandle。两者的ANR reason不一样,最终走的流程一样,都是走doNotifyAnrLockedInterruptible()
触发ANR。
// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// 从processNoFocusedWindowAnrLocked()走过来的流程
void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
// 这个流程走过来的ANR类型是xxx does not have a focused window
std::string reason = android::base::StringPrintf("%s does not have a focused window",
application->getName().c_str());
// 更新ANR信息
updateLastAnrStateLocked(application, reason);
// 构建CommandEntry,在dispatchOnce时执行命令
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = application;
commandEntry->inputChannel = nullptr;
commandEntry->reason = std::move(reason);
// 发送命令到mCommandQueue,执行doNotifyAnrLockedInterruptible()
postCommandLocked(std::move(commandEntry));
}
// 在processAnrsLocked()时没有走processNoFocusedWindowAnrLocked()后走到的流程
void InputDispatcher::onAnrLocked(const Connection& connection) {
// 前面走了startDispatchCycleLocked()流程,waitQueue不为空,如果为空,说明已经正常处理,此时不触发ANR
if (connection.waitQueue.empty()) {
ALOGI("Not raising ANR because the connection %s has recovered",
connection.inputChannel->getName().c_str());
return;
}
DispatchEntry* oldestEntry = *connection.waitQueue.begin();
const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
// 这个流程的ANR类型是Application is not responding. Waited 5000ms for xxx
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
connection.inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
// 更新ANR信息
updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
reason);
// 构建CommandEntry,在dispatchOnce时执行命令
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = nullptr;
commandEntry->inputChannel = connection.inputChannel;
commandEntry->reason = std::move(reason);
// 发送命令到mCommandQueue,执行doNotifyAnrLockedInterruptible()
postCommandLocked(std::move(commandEntry));
}
// 触发ANR时走到流程
void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
sp<IBinder> token =
commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
mLock.unlock();
// 通知system_server出现ANR
const nsecs_t timeoutExtension =
mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
mLock.lock();
// 如果通知system_server出现ANR时出现一次,则timeoutExtension为0,表示ANR被abort,
if (timeoutExtension > 0) {
// 更新ANR相关信息,等待下一次ANR
extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
} else {
// 已经触发ANR
sp<Connection> connection = getConnectionLocked(token);
if (connection == nullptr) {
return;
}
// 已经出现ANR,停止对这个connection派发事件
cancelEventsForAnrLocked(connection);
}
}
通过JNI通知system_server出现了ANR,最后调到appNotResponding()
方法
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<IBinder>& token, const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyANR");
#endif
ATRACE_CALL();
JNIEnv* env = jniEnv();
ScopedLocalFrame localFrame(env);
jobject inputApplicationHandleObj =
getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
jobject tokenObj = javaObjectForIBinder(env, token);
jstring reasonObj = env->NewStringUTF(reason.c_str());
// call到InputManagerCallback的notifyANR()方法
jlong newTimeout = env->CallLongMethod(mServiceObj,
gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
reasonObj);
// 如果出现异常,返回0给到InputDispatcher重新计时
if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
newTimeout = 0; // abort dispatch
} else {
assert(newTimeout >= 0);
}
return newTimeout;
}
system_server代码流程如下:
// frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) {
final long startTime = SystemClock.uptimeMillis();
try {
// notifyANRInner()
// ==> ActivityManagerService.inputDispatchingTimedOut()
// ==> ProcessRecord.appNotResponding()
// 最终通过appNotResponding()弹出应用无响应的对话框
return notifyANRInner(inputApplicationHandle, token, reason);
} finally {
// Log the time because the method is called from InputDispatcher thread. It shouldn't
// take too long that may affect input response time.
Slog.d(TAG_WM, "notifyANR took " + (SystemClock.uptimeMillis() - startTime) + "ms");
}
}
结语:
理清整个流程后,要处理这类问题,主要是要分析setFocusedApplication
和setInputWindows
流程有没有走到。其中的逻辑主要集中在WindowManagerService,涉及应用生命周期、窗口可见性变化等逻辑。应用端做一手分析时,需要确保生命周期走到了onResume,界面处于可见状态。在应用生命周期正常的情况下,系统侧分析就需要从多个状态去判断是否正常,其中很重要的一个方法是canReceiveKeys()
,其中的逻辑涉及较多,就不在此文介绍了。
网友评论