赞
踩
作者:王小二
有好多人向我咨询过Input ANR问题,说实话,我也是一直无法彻底的解释清楚,我下决心要彻底搞懂这块知识点。
1.InputReader线程一旦发现有新的event,判断mInBoundQueue是否为空,如果为空,设置wakeup = true
2.添加event到mInBoundQueue,如果wakeup==true,唤醒InputDispatcher的mLooper
a.发现mPendingEvnet为空且mInBoundQueue不为空,从mInBoundQueue获取一个event,并赋值给mPendingEvnet,走到第3步 b.发现mPendingEvnet不为空,走第3步 c.发现mPendingEvnet为空且mInBoundQueue为空,回到第1步休眠
主线程卡顿主要是导致的InputDispatcher线程中的正常流程第6步无法完成。
第1步,第2步是一样的
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;
- }
- }

因为无法发送event2,releasePendingEventLocked就不会触发,mPendingEvnet就会保留发送失败的event2。
情况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。
扫码或长按关注
回复「 加群 」进入技术群聊
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。