Android ANR视角InputDispatcher
作者:王小二
前言
有好多人向我咨詢過Input ANR問題,說實(shí)話,我也是一直無法徹底的解釋清楚,我下決心要徹底搞懂這塊知識(shí)點(diǎn)。
話不多說先上圖
一個(gè)event的正常流程
InputReader線程
1.InputReader線程一旦發(fā)現(xiàn)有新的event,判斷mInBoundQueue是否為空,如果為空,設(shè)置wakeup = true
2.添加event到mInBoundQueue,如果wakeup==true,喚醒InputDispatcher的mLooper
InputDispatcher線程
1.沒有事做的時(shí)候,mLooper.pollOnce(timeoutMillis)休眠, timeoutMillis為下次喚醒的delay時(shí)間。
2.mLooper被喚醒
a.發(fā)現(xiàn)mPendingEvnet為空且mInBoundQueue不為空,從mInBoundQueue獲取一個(gè)event,并賦值給mPendingEvnet,走到第3步 b.發(fā)現(xiàn)mPendingEvnet不為空,走第3步 c.發(fā)現(xiàn)mPendingEvnet為空且mInBoundQueue為空,回到第1步休眠
3.檢查當(dāng)前的window是否可以接收mPendingEvnet,正常情況下是OK的,異常的情況,我們后面討論。
4.通過InputChannel分發(fā)mPendingEvnet到APP層后, mPendingEvnet保存到waitQueue
5.發(fā)送成功后releasePendingEventLocked(mPendingEvnet == null),并將mLooper的nextWakeupTime設(shè)置LONG_LONG_MIN,然后回到第1步。
6.當(dāng)App層處理完event后會(huì)發(fā)送一個(gè)finish信號(hào)過來,然后移除waitQueue中event,并喚醒mLooper,觸發(fā)第2步
Input ANR的發(fā)生的原因:主線程的卡頓
怎么理解這句話如何導(dǎo)致的ANR?
主線程卡頓主要是導(dǎo)致的InputDispatcher線程中的正常流程第6步無法完成。
假設(shè)event1的沒有完成第6步,這時(shí)候來了一個(gè)event2這個(gè)流程是怎么樣子的:
第1步,第2步是一樣的
第3步:
waitQueue不為空,導(dǎo)致checkWindowReadyForMoreInputLocked返回值不為空,觸發(fā)handleTargetsNotReadyLocked,然后將當(dāng)前時(shí)間+5s作為mInputTargetWaitTimeoutTime,并設(shè)置mInputTargetWaitTimeoutTime為mLooper下一次喚醒的時(shí)間
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) { //省略好多代碼,因?yàn)椴恢挂环N請(qǐng)款,我們只分析一種 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) { //省略好多代碼 //設(shè)置第一次卡頓的flag后面進(jìn)來就不會(huì)設(shè)置了 mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; mInputTargetWaitStartTime = currentTime; //設(shè)置mInputTargetWaitTimeoutTime為當(dāng)前時(shí)間+5s mInputTargetWaitTimeoutTime = currentTime + timeout;//timeout = 5s //省略好多代碼 } //如何當(dāng)前的時(shí)候大于mInputTargetWaitTimeoutTime就出現(xiàn)ANR,默認(rèn)第一次進(jìn)來是走else if (currentTime >= mInputTargetWaitTimeoutTime) { onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, mInputTargetWaitStartTime, reason); *nextWakeupTime = LONG_LONG_MIN; return INPUT_EVENT_INJECTION_PENDING; } else { //將mInputTargetWaitTimeoutTime下一次wakeup的時(shí)間 if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { *nextWakeupTime = mInputTargetWaitTimeoutTime; } return INPUT_EVENT_INJECTION_PENDING; } }第4步:
因?yàn)闊o法發(fā)送event2,releasePendingEventLocked就不會(huì)觸發(fā),mPendingEvnet就會(huì)保留發(fā)送失敗的event2。
第5步:
情況A:在mInputTargetWaitTimeoutTime之前event1完成了常規(guī)的操作中的第6步,發(fā)送finish信號(hào),就會(huì)喚醒mLooper,然后繼續(xù)處理mPendingEvnet,也就是event2,因?yàn)閣aitQueue已經(jīng)為空了,那么event2就會(huì)按照正常流程的處理了
情況B:在mInputTargetWaitTimeoutTime之前event1沒有完成常規(guī)的操作第6步,這時(shí)候mLooper被handleTargetsNotReadyLocked中設(shè)置的wakeuptime所喚醒,然后繼續(xù)處理mPendingEvnet,也就是event2,因?yàn)閣aitQueue不為空,event1還在,所以又會(huì)觸發(fā)handleTargetsNotReadyLocked,這一次只會(huì)走以下代碼,然后觸發(fā)ANR
if (currentTime >= mInputTargetWaitTimeoutTime) { onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, mInputTargetWaitStartTime, reason); *nextWakeupTime = LONG_LONG_MIN; return INPUT_EVENT_INJECTION_PENDING; }總結(jié)
Input ANR是所有ANR中最難理解的一種ANR,我只分析了其中一種情況的Input ANR,想要了解所有Input ANR,只需要在源碼中搜索handleTargetsNotReadyLocked出現(xiàn)的位置,結(jié)合代碼看就知道了。
記住一句話:InputDispatcher永遠(yuǎn)只能單線程處理一個(gè)mPendingEvent,如果分發(fā)失敗,下一次會(huì)繼續(xù)分發(fā)同一個(gè)mPendingEvent。
掃碼或長(zhǎng)按關(guān)注
回復(fù)「?加群?」進(jìn)入技術(shù)群聊
總結(jié)
以上是生活随笔為你收集整理的Android ANR视角InputDispatcher的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。