之前咱们讲过由于事情没有得到及时处理,引起的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增加完窗口之后。
在这个过程中,很有很多的生命周期流程,包含前一个Activity的onPause,Applicaiton的attachBaseContext, onCreate, Activity的onCreate,onStart,onResume。所有办法加起来耗时不能超过5s,否则很容易产生ANR。
- window被设置成了no_focusable,无法响应焦点。
- 假如误将一个window设置成no_focusable,则窗口无法成为focusedWindow,也可能导致ANR的产生。
- 不过这种状况一般比较少呈现。