Android输入系统之InputDispatcher(AndroidV)

一:概述

    由前一篇文章Android输入系统之InputReader(AndroidV)-优快云博客可知,当InputReader读取到input事件后,会唤醒InputDispatcher线程来进行事件分发。下面来看一下InputDispatcher是如何进行事件分发的。

二:input事件分发

2.1 InputDispatcher.dispatchOnce

  当InputDispatcher被唤醒后,会执行dispatchOnce来开始分发事件

//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LLONG_MAX;
    { // acquire lock
        ...
        if (!haveCommandsLocked()) {
            //分发事件
            dispatchOnceInnerLocked(/*byref*/ nextWakeupTime);
        }
        ...
        //判断是否有anr发生
        const nsecs_t nextAnrCheck = processAnrsLocked();
        ...
    } // release lock

    ...
    //进入epoll_wait
    mLooper->pollOnce(timeoutMillis);
}

2.2 InputDispatcher.dispatchOnceInnerLocked

  • 判断事件分发是否被冻结,如果冻结则拦截事件,不进行分发
  • 从mInboundQueue队列中取出第一个事件,赋值给mPendingEvent
  • 根据事件类型(motion、key等),分别调用不同的方法继续input事件分发
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) {
    ...
    //判断事件分发是否被冻结(例如应用启动过程中,点击应用区域是不会有事件上报的,在这里被拦截了)
    if (mDispatchFrozen) {
        return;
    }
    ...
    //有待处理的事件
    if (!mPendingEvent) {
        if (mInboundQueue.empty()) {
            ...
        } else {
            //取出mInboundQueue队列中的第一个事件,赋值给mPendingEvent
            mPendingEvent = mInboundQueue.front();
            //删除队列中的第一个事件
            mInboundQueue.pop_front();
            ...
        }
        ...
    }
    ...
    DropReason dropReason = DropReason::NOT_DROPPED;
    ...
    //判断将要分发的事件类型
    switch (mPendingEvent->type) {
        ...
        case EventEntry::Type::MOTION: {
            std::shared_ptr<const MotionEntry> motionEntry =
                    std::static_pointer_cast<const MotionEntry>(mPendingEvent);
            //过时事件
            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
                ...
            }
            ...
            //分发事件
            done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
            break;
        }
        ...
    }
    ...
    if (done) {
        //事件需要被丢弃
        if (dropReason != DropReason::NOT_DROPPED) {
            dropInboundEventLocked(*mPendingEvent, dropReason);
        }
        ...
        //释放pending事件
        releasePendingEventLocked();
        ...
    }
}

2.3 InputDispatcher.dispatchMotionLocked

  motion事件会调用dispatchMotionLocked函数来继续事件分发流程。通过findTouchedWindowTargetsLocked找到touch事件对应的window,再调用dispatchEventLocked继续分发事件

//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
bool InputDispatcher::dispatchMotionLocked(...) {
    ...
    //丢弃所有需要丢弃的事件,停止事件分发
    if (*dropReason != DropReason::NOT_DROPPED) {
        ...
        return true;
    }
    
    const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER);
    ...
    if (isPointerEvent) {
        if (mDragState &&
            (entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            //如果是拖拽事件,阻止down事件下发直到拖拽结束
            pilferPointersLocked(mDragState->dragWindow->getToken());
        }
        //寻找touch事件对应的window
        inputTargets =
                findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult);
    } else {
        ...
    }
    //分发motion事件
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

2.4 InputDispatcher.findTouchedWindowTargetsLocked

  •   通过displayId/x/y/isStylus找到motion事件对应的window,如果没有则把第一个前台窗口当做对应窗口
  •   窗口有效性的判定:是否处于可接收事件的状态;是否可见;是否处于displayId对应的display中;事件的x/y坐标是否在window可点击坐标范围内等
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
        nsecs_t currentTime, const MotionEntry& entry,
        InputEventInjectionResult& outInjectionResult) {
    ...
    std::vector<InputTarget> targets;
    //获取touch事件对应的displayId
    const ui::LogicalDisplayId displayId = entry.displayId;
    ...
    TouchState tempTouchState;
    ...
    bool isSplit = shouldSplitTouch(tempTouchState, entry);
    //判断是否是悬停action
    const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
                                maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
                                maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
    //判断是否是down事件
    const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
            (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
    //判断是否是新手势
    const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
            maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
            maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
    ...
    //如果是新手势,把isSplit置为false
    if (newGesture) {
        isSplit = false;
    }
    ...
    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
        //通过entry找到此次touch事件的x/y坐标
        const auto [x, y] = resolveTouchedPosition(entry);
        ...
 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值