首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[015]ANR视角InputDispatcher

[015]ANR视角InputDispatcher

作者头像
王小二
发布2020-06-08 10:47:16
8000
发布2020-06-08 10:47:16
举报

前言

有好多人向我咨询过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。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 话不多说先上图
  • 一个event的正常流程
    • InputReader线程
      • InputDispatcher线程
      • Input ANR的发生的原因:主线程的卡顿
        • 怎么理解这句话如何导致的ANR?
          • 假设event1的没有完成第6步,这时候来了一个event2这个流程是怎么样子的:
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档