[015]ANR視角InputDispatcher

前言

有好多人向我咨詢過Input ANR問題,說實話,我也是一直無法徹底的解釋清楚,我下決心要徹底搞懂這塊知識點。

話不多說先上圖

流程圖

一個event的正常流程

InputReader線程

1.InputReader線程一旦發現有新的event,判斷mInBoundQueue是否為空,如果為空,設置wakeup = true

2.添加event到mInBoundQueue,如果wakeup==true,喚醒InputDispatcher的mLooper

InputDispatcher線程

1.沒有事做的時候,mLooper.pollOnce(timeoutMillis)休眠, timeoutMillis為下次喚醒的delay時間。
2.mLooper被喚醒

a.發現mPendingEvnet為空且mInBoundQueue不為空,從mInBoundQueue獲取一個event,并賦值給mPendingEvnet,走到第3步
b.發現mPendingEvnet不為空,走第3步
c.發現mPendingEvnet為空且mInBoundQueue為空,回到第1步休眠

3.檢查當前的window是否可以接收mPendingEvnet,正常情況下是OK的,異常的情況,我們后面討論。
4.通過InputChannel分發mPendingEvnet到APP層后, mPendingEvnet保存到waitQueue
5.發送成功后releasePendingEventLocked(mPendingEvnet == null),并將mLooper的nextWakeupTime設置LONG_LONG_MIN,然后回到第1步。
6.當App層處理完event后會發送一個finish信號過來,然后移除waitQueue中event,并喚醒mLooper,觸發第2步

Input ANR的發生的原因:主線程的卡頓

怎么理解這句話如何導致的ANR?

主線程卡頓主要是導致的InputDispatcher線程中的正常流程第6步無法完成。

假設event1的沒有完成第6步,這時候來了一個event2這個流程是怎么樣子的:

第1步,第2步是一樣的

第3步:

waitQueue不為空,導致checkWindowReadyForMoreInputLocked返回值不為空,觸發handleTargetsNotReadyLocked,然后將當前時間+5s作為mInputTargetWaitTimeoutTime,并設置mInputTargetWaitTimeoutTime為mLooper下一次喚醒的時間

std::string reason = checkWindowReadyForMoreInputLocked(currentTime,
         touchedWindow.windowHandle, entry, "touched");
if (!reason.empty()) {//reason不等于空
        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                 NULL, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());
        goto Unresponsive;
}

std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
        const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
        const char* targetType) {
        //省略好多代碼,因為不止一種請款,我們只分析一種
        if (!connection->waitQueue.isEmpty()
                && currentTime >= connection->waitQueue.head->deliveryTime
                        + STREAM_AHEAD_EVENT_TIMEOUT) {
            return StringPrintf("Waiting to send non-key event because the %s window has not "
                    "finished processing certain input events that were delivered to it over "
                    "%0.1fms ago.  Wait queue length: %d.  Wait queue head age: %0.1fms.",
                    targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
                    connection->waitQueue.count(),
                    (currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);
        }
    return "";
}


int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
        const EventEntry* entry,
        const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t* nextWakeupTime, const char* reason) {
        //省略好多代碼
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
            //省略好多代碼
            //設置第一次卡頓的flag后面進來就不會設置了
            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
            mInputTargetWaitStartTime = currentTime;
            //設置mInputTargetWaitTimeoutTime為當前時間+5s
            mInputTargetWaitTimeoutTime = currentTime + timeout;//timeout = 5s
            //省略好多代碼
        }

    //如何當前的時候大于mInputTargetWaitTimeoutTime就出現ANR,默認第一次進來是走else
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);
        *nextWakeupTime = LONG_LONG_MIN;
        return INPUT_EVENT_INJECTION_PENDING;
    } else {
        //將mInputTargetWaitTimeoutTime下一次wakeup的時間
        if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
            *nextWakeupTime = mInputTargetWaitTimeoutTime;
        }
        return INPUT_EVENT_INJECTION_PENDING;
    }
}
第4步:

因為無法發送event2,releasePendingEventLocked就不會觸發,mPendingEvnet就會保留發送失敗的event2。

第5步:

情況A:在mInputTargetWaitTimeoutTime之前event1完成了常規的操作中的第6步,發送finish信號,就會喚醒mLooper,然后繼續處理mPendingEvnet,也就是event2,因為waitQueue已經為空了,那么event2就會按照正常流程的處理了

情況B:在mInputTargetWaitTimeoutTime之前event1沒有完成常規的操作第6步,這時候mLooper被handleTargetsNotReadyLocked中設置的wakeuptime所喚醒,然后繼續處理mPendingEvnet,也就是event2,因為waitQueue不為空,event1還在,所以又會觸發handleTargetsNotReadyLocked,這一次只會走以下代碼,然后觸發ANR

if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);
        *nextWakeupTime = LONG_LONG_MIN;
        return INPUT_EVENT_INJECTION_PENDING;
    }

總結

Input ANR是所有ANR中最難理解的一種ANR,我只分析了其中一種情況的Input ANR,想要了解所有Input ANR,只需要在源碼中搜索handleTargetsNotReadyLocked出現的位置,結合代碼看就知道了。

記住一句話:InputDispatcher永遠只能單線程處理一個mPendingEvent,如果分發失敗,下一次會繼續分發同一個mPendingEvent。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。