Android7 Input(五)InputDispatcher

概述

本文主要描述了Android Input框架中的InputDispatcher的功能和数据处理流程。InputDispatcher的功能总结成一句话就是处理InputReader传递过来的事件并将事件进行再次封装为一个InputDispatcher事件然后传递给App进行处理,当App处理完成后,通知InputDispatcher模块清理InputDispatcher上报的事件,即完成整个输入事件的处理流程。

本文涉及的源码路径

frameworks/native/services/inputflinger/InputDispatcher.cpp

frameworks/native/services/inputflinger/InputReader.cpp

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

InputDispatcher的初始化

InputDispatcher的初始化由InputManager完成,如下所示:

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    /* 创建事件分发对象 */
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    /* 创建事件获取对象 */
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

InputDispatcher初始化中传递了一个dispatcherPolicy参数,该参数是NativeInputManager对象,如下所示:

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
   ......
    mInputManager = new InputManager(eventHub, this, this);
}

然后,我们进入InputDispatcher类的构造函数,如下所示:

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
    mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    // 创建Looper
    mLooper = new Looper(false);

    mKeyRepeatState.lastKeyEntry = NULL;

    policy->getDispatcherConfiguration(&mConfig);
}

初始化过程比较简单,最关键就是创建了一个自己的Looper对象处理自己的消息,感兴趣的同学可以研究一下Looper的实现,这里我们不再详细描述其作用。

InputDispatcher的运行

InputDispatcher的运行在InputDispatcherThread线程中,如下所示:

void InputManager::initialize() {
    ......
    /* 创建事件分发线程 */
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

status_t InputManager::start() {
    /* 开启事件分发线程 */
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
   ......
    return OK;
}

调用线程运行回调接口,如下所示:

 void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        // 如果Command队列为空的情况下,才处理mInboundQueue队列中的事件
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        // 处理command队列
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    // 计算epoll阻塞等待的超时时间
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    // 阻塞等待timeoutMillis
    mLooper->pollOnce(timeoutMillis);
}

InputDispatcher的线程回调逻辑比较简单,主要逻辑如下:

1、优先处理Command队列中的事件,InputDispatcher将所有处理的事务,封装为一个Command事项加入Command队列等待处理。实现了事件的异步处理,并不需要同步等待处理结果。

2、当Command队列为空时,从InputDispatcher的事件管理队列mInboundQueue中获取事件(本质上是InputReader上报的事件)进行处理,处理的过程后面的章节有详细描述;

3、如果mInboundQueue队列也为空,则InputDispacher进入阻塞等待事件的到来;

InputDispatcher事件分发流程

当InputDispatcher开始运行时,Command队列为空,mInboundQueue队列也为空,InputDispatcher进入阻塞等待事件的到来。我们在前一个章节讲述过,InputReader最终将事件传递给InputDispatcher的notify*接口,比如触摸屏幕调用noifyMotion接口,如下所示:

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    ....
        needWake = enqueueInboundEventLocked(newEntry);
    ....
    if (needWake) {
        mLooper->wake();
    }
}

这个函数的接口很简单,将触摸事件加入到mInboundQueue队列调用mLooper-wake()唤醒InputDispatcher线程继续执行,最终进入dispatchOnceInnerLocked接口,如下所示:

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

   ....
    if (!mPendingEvent) {
        // 如果分发事件队列为空
        if (mInboundQueue.isEmpty()) {
            ....
        } else {
            // Inbound queue has at least one entry.
            // 从mInboundQueue队列中取出一个待分发事件
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }

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

        // Get ready to dispatch the event.
        // 复位anr时间
        resetANRTimeoutsLocked();
    }

    ......
    switch (mPendingEvent->type) {
    ......
    case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        // 分发触摸事件
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }

    default:
        ALOG_ASSERT(false);
        break;
    }
    // 分发完成,进行清理工作
    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;

        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

dispatchOnceInnerLocked接口的逻辑如下:

1、从mInboundQueue队列中取出一个待处理的事件mPendingEvent;

2、重置ANR时间;

3、根据事件的类型,调用相应的接口进行处理,对于触摸屏设备来说,调用dispatchMotionLocked()进行处理;

4、事件处理完成后, 释放entry;

我们主要讲述触摸屏事件的处理,因此进入dispatchMotionLocked()接口,进一步进行分析

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Preprocessing.
   ......
    bool conflictingPointerActions = false;
    int32_t injectionResult;
    if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)
        // 发现目标窗口
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

   ......
    // 开始向App分发
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

dispatchMotionLocked接口的逻辑主要如下:

1、找到事件分发的目标窗口,也就是响应事件的焦点窗口,关于目标窗口的寻找这里不再展开描述。

2、找到目标窗口后,执行dispatchEventLocked()开始向App进程发送事件,实现如下:

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
   ......
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

        // 根据inputTarget.inputChannel找到连接目标
        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            // 找到链接通道
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            // 开始向连接通道分发事件
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } else {
#if DEBUG_FOCUS
            ALOGD("Dropping event delivery to target with channel '%s' because it "
                    "is no longer registered with the input dispatcher.",
                    inputTarget.inputChannel->getName().string());
#endif
        }
    }
}

dispatchEventLocked的逻辑如下:

1、找到与App连接的InputChannel通道(本质上是Unix 域socket套接字);

2、调用prepareDispatchCycleLocked接口,开始分发;

prepareDispatchCycleLocked的接口, 我们直接进入核心实现如下:

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
  ......
    // Not splitting.  Enqueue dispatch entries for the event as is.
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

enqueueDispatchEntriesLocked的实现:

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    bool wasEmpty = connection->outboundQueue.isEmpty();

    // Enqueue dispatch entries for the requested modes.
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_IS);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);

    // If the outbound queue was previously empty, start the dispatch cycle going.
    // connection连接输出通道队列不为空,开始向App分发事件
    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}

这个接口,多次调用enqueueDispatchEntryLocked(),为了便于阅读,我们只保留了核心代码,该接口的实现如下所示:

void InputDispatcher::enqueueDispatchEntryLocked(
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_t dispatchMode) {
   ......
    // 重新构造一个DispatchEntry分发实体
    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
            inputTarget->scaleFactor);

   ......

    // Remember that we are waiting for this dispatch to complete.
    if (dispatchEntry->hasForegroundTarget()) {
        incrementPendingForegroundDispatchesLocked(eventEntry);
    }

    // Enqueue the dispatch entry.
    // 将dispatchEntry加入到连接通道的输出队列的队尾
    connection->outboundQueue.enqueueAtTail(dispatchEntry);
    traceOutboundQueueLengthLocked(connection);
}

enqueueDispatchEntryLocked接口的主要核心逻辑就是将eventEntry事件封装为一个dispatchEntry实体,然后加入到连接通道的输出队列;

当dispatchEntry事件被加入到connect的输出队列后,我返回到enqueueDispatchEntriesLocked接口的尾部,调用startDispatchCycleLocked()正式向App进程发送输入事件:

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
    .......
    // 连接通道正常,并且输出队列不为空
    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        // 分发Entry
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        // 设置开始分发的时间
        dispatchEntry->deliveryTime = currentTime;

        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
     
        case EventEntry::TYPE_MOTION: {
            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);

           ......
            // Publish the motion event.
            // 分发触摸事件
            status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
                    motionEntry->deviceId, motionEntry->source,
                    dispatchEntry->resolvedAction, motionEntry->actionButton,
                    dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
                    motionEntry->metaState, motionEntry->buttonState,
                    xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
                    motionEntry->downTime, motionEntry->eventTime,
                    motionEntry->pointerCount, motionEntry->pointerProperties,
                    usingCoords);
            break;
        }

        default:
            ALOG_ASSERT(false);
            return;
        }

        ......
        }

        // Re-enqueue the event on the wait queue.
        // 将dispatchEntry移动到等待队列
        connection->outboundQueue.dequeue(dispatchEntry);
        traceOutboundQueueLengthLocked(connection);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        traceWaitQueueLengthLocked(connection);
    }
}

startDispatchCycleLocked()接口的实现:

1、 主要处理加入到connect输出队列中的dispachEntry,然后根据事件的类型,调用各自的处理接口,对于触摸屏幕来说,就是调用publishMotionEvent,如下所示:

status_t InputPublisher::publishMotionEvent(
       ......

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_MOTION;
    msg.body.motion.seq = seq;
    msg.body.motion.deviceId = deviceId;
    msg.body.motion.source = source;
    msg.body.motion.action = action;
    msg.body.motion.actionButton = actionButton;
    msg.body.motion.flags = flags;
    msg.body.motion.edgeFlags = edgeFlags;
    msg.body.motion.metaState = metaState;
    msg.body.motion.buttonState = buttonState;
    msg.body.motion.xOffset = xOffset;
    msg.body.motion.yOffset = yOffset;
    msg.body.motion.xPrecision = xPrecision;
    msg.body.motion.yPrecision = yPrecision;
    msg.body.motion.downTime = downTime;
    msg.body.motion.eventTime = eventTime;
    msg.body.motion.pointerCount = pointerCount;
    for (uint32_t i = 0; i < pointerCount; i++) {
        msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
        msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
    }
    // 向unix socket写入msg, 这样App进程就能夸进程可以读到数据
    return mChannel->sendMessage(&msg);
}

重新再次创建一个App可识别的消息msg,然后调用mChannel->sendMessage进行发送,关于InputChannel,我们下一篇文章进行讲述其实现原理(本质上是向unix socket发送数据);

2、发送完事件后,将dispatchEntry事件从输出队列移动到waitQueue队列;

至此,我们讲述完了InputDispatcher向App目标窗口分发事件的整个数据处理的流程。

InputDispatcher响应App发送的信息

当App处理完InputDispatcher上报的事件后,App通过输入通道InputChannel,告诉InputDispatcher处理结果,最终回调handleReceiveCallback()进行事件的后期处理,handleReceiveCallback在进行输入通道注册时,加入到InputDispatcher的looper监控中,实现如下:

// 注册输入通道
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
  ......
        // 将输入通道的文件描述符加入到mLooper监控
        // 当向输入通道写入数据时,回调handleReceiveCallback()接口
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

我们下一篇会讲解InputDispatcher与App进行交互的桥梁InputChannel的实现,这里不展开描述,我们只关注App处理完事件后,会向输入通道InputChannel写入信息,将事件的后期处理交给InputDispatcher进行处理,最后会回调handleReceiveCallback接口,如下所示:

int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
    InputDispatcher* d = static_cast<InputDispatcher*>(data);

    { // acquire lock
        AutoMutex _l(d->mLock);
       ......
        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
         ......
            for (;;) {
                uint32_t seq;
                bool handled;
                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
                if (status) {
                    break;
                }
                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
                gotOne = true;
            }
            if (gotOne) {
                d->runCommandsLockedInterruptible();
                if (status == WOULD_BLOCK) {
                    return 1;
                }
            }

            notify = status != DEAD_OBJECT || !connection->monitor;
            if (notify) {
                ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                        connection->getInputChannelName(), status);
            }
        } else {
            // Monitor channels are never explicitly unregistered.
            // We do it automatically when the remote endpoint is closed so don't warn
            // about them.
            notify = !connection->monitor;
            if (notify) {
                ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                        "events=0x%x", connection->getInputChannelName(), events);
            }
        }

        // Unregister the channel.
        d->unregisterInputChannelLocked(connection->inputChannel, notify);
        return 0; // remove the callback
    } // release lock
}

1、调用receiveFinishedSignal从App哪里获取数据,核心实现如下所示:

status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
   ......
    InputMessage msg;
    // 从输入通道获取msg
    status_t result = mChannel->receiveMessage(&msg);
    if (result) {
        *outSeq = 0;
        *outHandled = false;
        return result;
    }
    ......
    *outSeq = msg.body.finished.seq;
    *outHandled = msg.body.finished.handled;
    return OK;
}

2、调用finishDispatchCycleLocked,改接口最终调用onDispatchCycleFinishedLocked()进行事件的清理,核心实现如下所示:

void InputDispatcher::onDispatchCycleFinishedLocked(
        nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
    // 构造一个结束分发的command
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
    commandEntry->connection = connection;
    commandEntry->eventTime = currentTime;
    commandEntry->seq = seq;
    commandEntry->handled = handled;
}

构造一个Command. 并加入到InputDispatcher的mCommandQueue队列中,

3、当接受完所有的App发送的数据后,跳出死循环,进入runCommandsLockedInterruptible进行处理,实现如下:

bool InputDispatcher::runCommandsLockedInterruptible() {
    if (mCommandQueue.isEmpty()) {
        return false;
    }

    do {
        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();

        Command command = commandEntry->command;
        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'

        commandEntry->connection.clear();
        delete commandEntry;
    } while (! mCommandQueue.isEmpty());

    return true;
}

该函数的实现非常简单,处理mCommandQueue队列中的command,然后调用command注册的回调。对于App下发的信息处理回调接口是doDispatchCycleFinishedLockedInterruptible,实现如下:

void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
        CommandEntry* commandEntry) {
  
    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
    if (dispatchEntry) {
        bool restartEvent;
        if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
            restartEvent = afterKeyEventLockedInterruptible(connection,
                    dispatchEntry, keyEntry, handled);
        } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
            MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
            restartEvent = afterMotionEventLockedInterruptible(connection,
                    dispatchEntry, motionEntry, handled);
        } else {
            restartEvent = false;
        }

        // Dequeue the event and start the next cycle.
        // Note that because the lock might have been released, it is possible that the
        // contents of the wait queue to have been drained, so we need to double-check
        // a few things.
        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
            // 从waitQueue队列中删除
            connection->waitQueue.dequeue(dispatchEntry);
            // 是否需要事件重发
            if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
                connection->outboundQueue.enqueueAtHead(dispatchEntry);
                traceOutboundQueueLengthLocked(connection);
            } else {
                // 释放dispatchEntry
                releaseDispatchEntryLocked(dispatchEntry);
            }
        }

        // Start the next dispatch cycle for this connection.
        // 继续分发
        startDispatchCycleLocked(now(), connection);
    }
}

doDispatchCycleFinishedLockedInterruptible()接口的主要逻辑就是:

1、从waitQueue队列中找到App处理完成的InputDispatcher,然后释放InputDispatcher占用的资源,标记该事件处理完毕;

2、检查是否需要重发事件,重发逻辑不展开描述;

3、继续事件分发;

至此,我们讲述完了App通知InputDispatcher进行事件的清理工作。

总结

本文主要讲述了InputDispatcher事件向App分发和App处理完事件后告知InputDispatcher的处理流程。下一篇讲述InputDispatcher和App进行交互的桥梁InputChannel的实现;

<think>好的,我现在需要帮用户梳理AndroidInput流程文档。用户可能是个开发者,遇到了输入事件处理的问题,或者想深入理解Android系统如何处理触摸和按键事件。我应该从整体流程开始,逐步分解各个部分,确保逻辑清晰。 首先,我得回忆一下Android系统的Input处理流程。大致分为硬件层、内核层、系统服务层和应用层。硬件检测到触摸或按键事件后,通过设备驱动传到Linux内核,生成原始事件。接着,系统服务如InputReader和InputDispatcher处理这些事件,InputReader读取事件并加工,InputDispatcher将事件分发给合适的窗口。 然后,要考虑窗口管理,比如WMS如何确定哪个窗口获得焦点,处理窗口层级和焦点切换。之后是应用层的处理,ViewRootImpl和View体系如何接收事件,经过onTouchEvent等方法传递,直到被消费。 可能还需要提到常见问题,比如事件拦截、多指触摸、ANR等,帮助用户实际开发中避免问题。用户可能需要实际案例或者流程图,但当前文本回复无法展示图表,所以要用文字描述清楚流程。 需要检查是否有遗漏的关键点,比如InputManagerService的作用,事件的分发顺序,以及Choreographer和VSync信号的关系,确保事件处理的同步。另外,不同Android版本是否有差异?可能用户使用的版本较新,但一般介绍通用流程,除非特别说明。 用户可能还想知道如何调试Input事件,比如使用getevent、sendevent命令,或者查看日志。这部分可以放在问题排查里。还要考虑是否需要涉及无障碍服务或输入法处理,但基础流程可能不需要,除非用户特别提到。 最后,确保结构清晰,分点讲解,从底层到上层,每部分的职责明确,让用户能一步步理解整个流程。避免使用过于专业的术语,必要时解释缩写,比如WMS、IMS等。检查是否有错误,比如InputDispatcher的分发逻辑是否正确,是否先找到目标窗口,再发送事件,应用处理超时导致ANR等。</think>以下是对 Android Input 流程的梳理文档,帮助你理解从物理输入(触摸、按键等)到应用层事件处理的完整流程: --- ### **1. 输入事件流程概览** Android 的输入流程分为 **硬件层 → 内核层 → 系统服务层 → 应用层**,核心模块包括: - **InputReader**:读取原始输入事件。 - **InputDispatcher**:分发事件到目标窗口。 - **WindowManagerService (WMS)**:管理窗口层级与焦点。 - **ViewRootImpl & View 体系**:处理事件传递与消费。 --- ### **2. 详细流程分解** #### **2.1 硬件层 → 内核层** - **硬件输入**:用户触摸屏幕或按下物理按键,硬件生成中断信号。 - **内核驱动**:设备驱动(如 `evdev`)将原始事件写入 `/dev/input/eventX` 节点,事件格式为 `struct input_event`。 #### **2.2 系统服务层 (Native)** 1. **InputReader 线程**: - 通过 `EventHub` 监听 `/dev/input` 下的输入设备节点。 - 读取原始事件(`RawEvent`),并转换为 `KeyEvent`/`MotionEvent` 等结构化数据。 - 处理输入设备状态(如屏幕方向、多点触控校准)。 2. **InputDispatcher 线程**: - 接收来自 `InputReader` 的事件。 - 通过 `WindowManagerService` 确定目标窗口(基于焦点窗口、触摸区域、窗口层级等)。 - 将事件发送到目标应用进程的 `InputChannel`(基于 `SocketPair` 通信)。 #### **2.3 应用层 (Java)** 1. **InputChannel 与 ViewRootImpl**: - 每个窗口(Activity/Dialog)通过 `ViewRootImpl` 注册 `InputChannel`。 - `InputEventReceiver`(如 `ViewRootImpl.ViewPostImeInputReceiver`)接收事件。 2. **事件分发流程**: - **ViewRootImpl** → **DecorView** → **Activity** → **Window** → **View 层级**。 - 核心方法: - `View.dispatchTouchEvent()`:事件进入 View 树。 - `ViewGroup.onInterceptTouchEvent()`:拦截事件。 - `View.onTouchEvent()`:最终消费事件。 --- ### **3. 关键模块详解** #### **3.1 InputDispatcher 分发逻辑** - **焦点窗口**:由 `WMS` 维护,决定按键事件的目标(如 `EditText` 获取焦点时接收输入)。 - **触摸事件目标**:根据触摸坐标和窗口 `Surface` 的位置计算目标窗口。 - **ANR 机制**:若应用 5 秒内未处理完事件,触发 `ANR`(`InputDispatcher` 监测超时)。 #### **3.2 应用层事件传递** - **ACTION_DOWN 起点**:决定后续事件序列的接收者。 - **事件拦截**:`ViewGroup` 可通过 `onInterceptTouchEvent()` 拦截事件。 - **事件消费**:若某个 `View` 的 `onTouchEvent()` 返回 `true`,则消费该事件。 --- ### **4. 常见问题与调试** #### **4.1 常见问题** - **事件丢失/冲突**:多个 View 未正确处理 `onTouchEvent` 返回值。 - **焦点异常**:动态添加/移除 View 时未正确请求焦点。 - **ANR**:主线程阻塞导致事件未及时处理。 #### **4.2 调试工具** - **`getevent/sendevent`**:查看/模拟底层输入事件。 - **`adb shell dumpsys input`**:查看 InputDispatcher 状态、焦点窗口。 - **日志过滤**:搜索 `InputDispatcher` 或 `ViewRootImpl` 相关日志。 --- ### **5. 流程图(文字描述)** ``` 硬件输入 → 内核生成事件 → InputReader 解析 → InputDispatcher 分发 → WMS 计算目标窗口 → 通过 Socket 发送到应用 → ViewRootImpl 接收 → Activity 传递 → View 层级处理 ``` --- 希望这份文档能帮助你理解 Android 输入事件的核心流程。如果需要更深入的源码分析或具体场景案例,可以进一步补充问题!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值