Android开发No Focused Window ANR产生原理解析
作者:尹学姐
这篇文章主要为大家介绍了Android开发No Focused Window ANR产生原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
引言
之前我们讲过因为事件没有得到及时处理,引起的ANR问题。但这只是Input Dispatching Timeout中的一种情况,还有一种情况,在我们应用中出现的也很常见,就是No Focused Window ANR,这个又是在哪些情况下产生的呢?
由之前的文章,我们知道,点击事件都是由InputDispatcher来分发的,我们直接来看InputDispatcher的源码。
No Focused Window ANR如何产生
如果是Key事件,或Motion事件,都需要找到焦点窗口取处理,都会调用到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) { 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; *nextWakeupTime = *mNoFocusedWindowTimeoutTime; // 返回INPUT_EVENT_INJECTION_PENDING表示dispatchKeyLocked()或者dispatchMotionLocked()为false return INPUT_EVENT_INJECTION_PENDING; } else if (currentTime > *mNoFocusedWindowTimeoutTime) { // Already raised ANR. Drop the event return INPUT_EVENT_INJECTION_FAILED; } else { // Still waiting for the focused window return INPUT_EVENT_INJECTION_PENDING; } } // 如果走到这个流程,说明没有ANR,清空mNoFocusedWindowTimeoutTime和mAwaitedFocusedApplication resetNoFocusedWindowTimeoutLocked(); return INPUT_EVENT_INJECTION_SUCCEEDED; }
主要逻辑:
- 如果focusedWindowHandle和focusedApplicationHandle都为null,一般发生在窗口切换的时候,返回INPUT_EVENT_INJECTION_FAILED,直接drop事件,不做处理
- 如果focusedWindowHandle为null,focusedApplicationHandle不为null,返回INPUT_EVENT_INJECTION_PENDING,在nextWakeupTime之后唤醒,检查是否发生ANR
- mNoFocusedWindowTimeoutTime:记录no focused window timeout的时间
- mAwaitedFocusedApplication:记录focusedApplicationHandle
- nextWakeupTime: 下次唤醒pollInner的时间
检查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; } } .... // 如果走到这个流程,ANR类型是xxx is not responding. Waited xxx ms for xxx // 这个地方,focusedWindowHandle和focusedApplicationHandle都是不为空的场景 onAnrLocked(*connection); return LONG_LONG_MIN; }
主要流程:
- 如果mNoFocusedWindowTimeoutTime有值,且mAwaitedFocusedApplication不为空
- 超时:调用processNoFocusedWindowAnrLocked触发ANR
- 未超时:更新检查时间
- 继续检查input事件是否超时,如果超时,则调用onAnrLocked触发ANR
processNoFocusedAnrLocked的流程
// 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()) { 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); }
主要流程:
- 在这个方法里面,再次检查focusedApplication
- 如果当前focusedApplication为空,或者和之前记录的mAwaitedFocusedApplication不一致,则说明窗口已经切换,不需要报ANR
- 再次检查focusedWindow是否未空
- 如果不为空,则不需要报ANR
- 检查都通过之后,才会调用onAnrLocked,报no Focused Window ANR
focusedApplication设置流程
// 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(); }
主要流程:
- 如果inputApplicationHandle与oldFocusedApplication,则要重置ANR计时
- 如果inputApplicationHandle不为空,则更新map中的值
- 如果inputApplicationHandle为空,则清除oldFocusedApplication
这个方法,是从AMS调过来的,主要流程如下图:
focusedWindow设置流程
// 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); } } // ...... }
这个方法,是从WMS调过来的,主要流程如下图:
ANR可能得原因
设置focusedApplication和focusedWindow中间时间差太长,在这个时间差内发生了ANR
设置focusedApplication发生在,resumeTopActivity,也就是am_set_resumed_activity的时候。
设置focusedWindow发生在,onResume之后,调用WMS的addView添加完窗口之后。
window被设置成了no_focusable,无法响应焦点。
如果误将一个window设置成no_focusable,则窗口无法成为focusedWindow,也可能导致ANR的发生。
以上就是Android开发No Focused Window ANR产生原理解析的详细内容,更多关于Android No Focused Window ANR的资料请关注脚本之家其它相关文章!