欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android開發(fā)No Focused Window ANR產(chǎn)生原理解析

 更新時間:2023年07月28日 10:40:12   作者:尹學(xué)姐  
這篇文章主要為大家介紹了Android開發(fā)No Focused Window ANR產(chǎn)生原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

之前我們講過因為事件沒有得到及時處理,引起的ANR問題。但這只是Input Dispatching Timeout中的一種情況,還有一種情況,在我們應(yīng)用中出現(xiàn)的也很常見,就是No Focused Window ANR,這個又是在哪些情況下產(chǎn)生的呢?

由之前的文章,我們知道,點擊事件都是由InputDispatcher來分發(fā)的,我們直接來看InputDispatcher的源碼。

No Focused Window ANR如何產(chǎn)生

如果是Key事件,或Motion事件,都需要找到焦點窗口取處理,都會調(diào)用到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都為空時表示當前無窗口,該事件會被丟棄,不會執(zhí)行dispatchEventLocked
    // 一般出現(xiàn)兩個都為空的場景,是在窗口切換的過程,此時不處理事件注入
    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賦值,觸發(fā)ANR時會檢查這個值是否為空,不為空才觸發(fā)ANR
            mNoFocusedWindowTimeoutTime = currentTime + timeout;
            // 把當前的focusedApplicationHandle賦值給mAwaitedFocusedApplication,觸發(fā)ANR時會檢查這個值是否為空,不為空才觸發(fā)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,一般發(fā)生在窗口切換的時候,返回INPUT_EVENT_INJECTION_FAILED,直接drop事件,不做處理
  • 如果focusedWindowHandle為null,focusedApplicationHandle不為null,返回INPUT_EVENT_INJECTION_PENDING,在nextWakeupTime之后喚醒,檢查是否發(fā)生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) {
            // 觸發(fā)ANR流程,此處觸發(fā)的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不為空
    • 超時:調(diào)用processNoFocusedWindowAnrLocked觸發(fā)ANR
    • 未超時:更新檢查時間
  • 繼續(xù)檢查input事件是否超時,如果超時,則調(diào)用onAnrLocked觸發(fā)ANR

processNoFocusedAnrLocked的流程

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::processNoFocusedWindowAnrLocked() {
    // 在觸發(fā)ANR前,再獲取一次當前的focusedApplication
    sp<InputApplicationHandle> focusedApplication =
            getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
    // 檢查觸發(fā)ANR時的條件是focusedApplication不為空
    // 如果此時focusedApplication為空,或者focusedApplication不等于前一個mAwaitedFocusedApplication表示已經(jīng)切換application focus,取消觸發(fā)ANR
    if (focusedApplication == nullptr ||
        focusedApplication->getApplicationToken() !=
                mAwaitedFocusedApplication->getApplicationToken()) {
        return; // The focused application has changed.
    }
    // 在觸發(fā)ANR前,再獲取一次當前的focusedWindowHandle 
    const sp<InputWindowHandle>& focusedWindowHandle =
            getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
    // 檢查觸發(fā)ANR時focusedWindowHandle為空,如果此時focusedWindowHandle不為空,取消觸發(fā)ANR
    if (focusedWindowHandle != nullptr) {
        return; // We now have a focused window. No need for ANR.
    }
    // 通過前面的判斷,還是無法攔截,說明該ANR無可避免,最終觸發(fā)ANR
    // 早期代碼沒有前面一系列的判斷,是直接觸發(fā)的ANR,會在性能較差的場景下出現(xiàn)誤判
    onAnrLocked(mAwaitedFocusedApplication);
}

主要流程:

  • 在這個方法里面,再次檢查focusedApplication
    • 如果當前focusedApplication為空,或者和之前記錄的mAwaitedFocusedApplication不一致,則說明窗口已經(jīng)切換,不需要報ANR
  • 再次檢查focusedWindow是否未空
    • 如果不為空,則不需要報ANR
  • 檢查都通過之后,才會調(diào)用onAnrLocked,報no Focused Window ANR

focusedApplication設(shè)置流程

// 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跟觸發(fā)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();
            // 走到這個流程會出現(xiàn)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調(diào)過來的,主要流程如下圖:

focusedWindow設(shè)置流程

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// 當VSYNC信號來了之后,會調(diào)用到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時會執(zhí)行
            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
        }
    }
    // ......
}

這個方法,是從WMS調(diào)過來的,主要流程如下圖:

ANR可能得原因

設(shè)置focusedApplication和focusedWindow中間時間差太長,在這個時間差內(nèi)發(fā)生了ANR

設(shè)置focusedApplication發(fā)生在,resumeTopActivity,也就是am_set_resumed_activity的時候。

設(shè)置focusedWindow發(fā)生在,onResume之后,調(diào)用WMS的addView添加完窗口之后。

window被設(shè)置成了no_focusable,無法響應(yīng)焦點。

如果誤將一個window設(shè)置成no_focusable,則窗口無法成為focusedWindow,也可能導(dǎo)致ANR的發(fā)生。

以上就是Android開發(fā)No Focused Window ANR產(chǎn)生原理解析的詳細內(nèi)容,更多關(guān)于Android No Focused Window ANR的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論