android window事件分发

本文详细探讨了Android系统的输入事件处理流程,包括窗口分发过程:用户事件经由设备文件传递到InputReaderThread,然后由InputDispatcherThread分发到Window。同时解析了事件注册过程,从ViewRootImpl构造到InputChannel的创建,再到WindowManagerService端的InputChannel注册。此外,还介绍了事件如何从Window发送到ViewRootImpl,重点分析了InputDispatcherThread的关键分发代码和焦点窗口的处理策略。

关于android输入事件,比如滑动,点击,按键涉及到的主要流程

事件的window分发过程

用户事件 -> /dev/下设备文件 -> InputReaderThread -> InputDispatherThread -> Window

事件的注册过程

ViewRootImpl构造(构造InputChannel)->WindowManager.openSession->WindowSession.addToDisplay->WindowManagerSerive端构造InputChannel->通过InputManager注册到InputDispatherThread

window到ViewRootImpl发送过程

InputDispatherThread选择window分发的关键代码

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();

    // Reset the key repeat timer whenever normal dispatch is suspended while the
    // device is in a non-interactive state.  This is to ensure that we abort a key
    // repeat if the device is just coming out of sleep.
    if (!mDispatchEnabled) {
        resetKeyRepeatLocked();
    }

    // If dispatching is frozen, do not process timeouts or try to deliver any new events.
    if (mDispatchFrozen) {
        if (DEBUG_FOCUS) {
            ALOGD("Dispatch frozen.  Waiting some more.");
        }
        return;
    }

    // Optimize latency of app switches.
    // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
    // been pressed.  When it expires, we preempt dispatch and drop all other pending events.
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }

    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    if (!mPendingEvent) {
        if (mInboundQueue.empty()) {
            if (isAppSwitchDue) {
                // The inbound queue is empty so the app switch key we were waiting
                // for will never arrive.  Stop waiting for it.
                resetPendingAppSwitchLocked(false);
                isAppSwitchDue = false;
            }

            // Synthesize a key repeat if appropriate.
            if (mKeyRepeatState.lastKeyEntry) {
                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                } else {
                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                    }
                }
            }

            // Nothing to do if there is no pending event.
            if (!mPendingEvent) {
                return;
            }
        } else {
            // Inbound queue has at least one entry.
            mPendingEvent = mInboundQueue.front();
            mInboundQueue.pop_front();
            traceInboundQueueLengthLocked();
        }

        // Poke user activity for this event.
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(*mPendingEvent);
        }
    }

    // Now we have an event to dispatch.
    // All events are eventually dequeued and processed this way, even if we intend to drop them.
    ALOG_ASSERT(mPendingEvent != nullptr);
    bool done = false;
    DropReason dropReason = DropReason::NOT_DROPPED;
    if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DropReason::POLICY;
    } else if (!mDispatchEnabled) {
        dropReason = DropReason::DISABLED;
    }

    if (mNextUnblockedEvent == mPendingEvent) {
        mNextUnblockedEvent = nullptr;
    }

    switch (mPendingEvent->type) {
        ......

        case EventEntry::Type::KEY: {
            KeyEntry* typedEntry = static_cast(mPendingEvent);
            if (isAppSwitchDue) {
                if (isAppSwitchKeyEvent(*typedEntry)) {
                    resetPendingAppSwitchLocked(true);
                    isAppSwitchDue = false;
                } else if (dropReason == DropReason::NOT_DROPPED) {
                    dropReason = DropReason::APP_SWITCH;
                }
            }
            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
                dropReason = DropReason::STALE;
            }
            if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
                dropReason = DropReason::BLOCKED;
            }
            done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }

        case EventEntry::Type::MOTION: {
            MotionEntry* typedEntry = static_cast(mPendingEvent);
            if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
                dropReason = DropReason::APP_SWITCH;
            }
            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
                dropReason = DropReason::STALE;
            }
            if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
                dropReason = DropReason::BLOCKED;
            }
            done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
    }

    ......
}

dispatchOnceInnerLocked 获取pendingEvent 判断是不同类型的事件,比如是按键事件,通过dispatchKeyLocked处理,触摸事件通过dispatchMotionLocked处理

按键事件处理,看到就是分发给focus window

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Preprocessing.
    ......

    // Identify targets.
    std::vector inputTargets;
    int32_t injectionResult =
            findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

    setInjectionResult(entry, injectionResult);
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }

    // Add monitor channels from event's or focused display.
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));

    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

findFocusedWindowTargetsLocked的实现主要就是从mFocusedWindowHandlesByDisplay取出对应的focus window

int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
                                                        const EventEntry& entry,
                                                        std::vector<InputTarget>& inputTargets,
                                                        nsecs_t* nextWakeupTime) {
    std::string reason;

    int32_t displayId = getTargetDisplayId(entry);
    sp<InputWindowHandle> focusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
    sp<InputApplicationHandle> focusedApplicationHandle =
            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);

    ......

    // Success!  Output targets.
    addWindowTargetLocked(focusedWindowHandle,
                          InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
                          BitSet32(0), inputTargets);

    // Done.
    return INPUT_EVENT_INJECTION_SUCCEEDED;
}

mFocusedWindowHandlesByDisplay何时设置的呢,在

/**
 * Called from InputManagerService, update window handle list by displayId that can receive input.
 * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
 * If set an empty list, remove all handles from the specific display.
 * For focused handle, check if need to change and send a cancel event to previous one.
 * For removed handle, check if need to send a cancel event if already in touch.
 */
void InputDispatcher::setInputWindowsLocked(
        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
    
    // Copy old handles for release if they are no longer present.
    const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);

    updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);

    sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
    bool foundHoveredWindow = false;
    for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
        // Set newFocusedWindowHandle to the top most focused window instead of the last one
        if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
            windowHandle->getInfo()->visible) {
            newFocusedWindowHandle = windowHandle;
        }
        if (windowHandle == mLastHoverWindowHandle) {
            foundHoveredWindow = true;
        }
    }

    if (!foundHoveredWindow) {
        mLastHoverWindowHandle = nullptr;
    }

    sp<InputWindowHandle> oldFocusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);

    if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
        if (oldFocusedWindowHandle != nullptr) {
            if (DEBUG_FOCUS) {
                ALOGD("Focus left window: %s in display %" PRId32,
                      oldFocusedWindowHandle->getName().c_str(), displayId);
            }
            sp<InputChannel> focusedInputChannel =
                    getInputChannelLocked(oldFocusedWindowHandle->getToken());
            if (focusedInputChannel != nullptr) {
                CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                                           "focus left window");
                synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
                enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
            }
            mFocusedWindowHandlesByDisplay.erase(displayId);
        }
        if (newFocusedWindowHandle != nullptr) {
            if (DEBUG_FOCUS) {
                ALOGD("Focus entered window: %s in display %" PRId32,
                      newFocusedWindowHandle->getName().c_str(), displayId);
            }
            mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
            enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);

            //MIUI ADD:
            resetNoFocusedWindowTimeoutLocked();
        }

        if (mFocusedDisplayId == displayId) {
            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
        }
    }

    std::unordered_map<int32_t, TouchState>::iterator stateIt =
            mTouchStatesByDisplay.find(displayId);
    if (stateIt != mTouchStatesByDisplay.end()) {
        TouchState& state = stateIt->second;
        for (size_t i = 0; i < state.windows.size();) {
            TouchedWindow& touchedWindow = state.windows[i];
            if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
                if (DEBUG_FOCUS) {
                    ALOGD("Touched window was removed: %s in display %" PRId32,
                          touchedWindow.windowHandle->getName().c_str(), displayId);
                }
                sp<InputChannel> touchedInputChannel =
                        getInputChannelLocked(touchedWindow.windowHandle->getToken());
                if (touchedInputChannel != nullptr) {
                    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                               "touched window was removed");
                    synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
                }
                state.windows.erase(state.windows.begin() + i);
            } else {
                ++i;
            }
        }
    }

    // Release information for windows that are no longer present.
    // This ensures that unused input channels are released promptly.
    // Otherwise, they might stick around until the window handle is destroyed
    // which might not happen until the next GC.
    for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
        if (!hasWindowHandleLocked(oldWindowHandle)) {
            if (DEBUG_FOCUS) {
                ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
            }
            oldWindowHandle->releaseChannel();
        }
    }
}

查看window状态命令

activity切换对应window状态

popwindow对应window状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值