写点什么

Android 底层事件分发机制 - 完结篇

作者:江湖修行
  • 2024-06-13
    北京
  • 本文字数:10748 字

    阅读完需:约 35 分钟

Android底层事件分发机制-完结篇

前序

书接上文,前面我们讲了下 Android 输入事件的全貌,从底层硬件产生事件,经过 IMS 的 InputReader 加工转换再经过 InputDspatcher 分发转给 WMS 中间层,再通过 WMS 定位到准确的 window 进行我们熟悉的上层事件处理。


上文我们详细讲解了 IMS 的初始化流程及底层事件的拦截处理流程,今天我们继续讲解 InputReader 处理完的事件怎么进行分发的。

InputDspatcher 分发事件

将封装的事件压入到队列中并唤醒 InputDispatchert 线程进行分发处理。

InputDispatcher 线程唤醒

status_t InputDispatcher::start() {        if (mThread) {            return ALREADY_EXISTS;        }        mThread = std::make_unique<InputThread>(                "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });        return OK;    }
复制代码


和 InputReader 一样运行在单独的线程,执行 threadLoop 时的回调函数 dispatchOnce


void InputDispatcher::dispatchOnce() {        nsecs_t nextWakeupTime = LONG_LONG_MAX;        { // acquire lock            std::scoped_lock _l(mLock);            mDispatcherIsAlive.notify_all();            //注释1            if (!haveCommandsLocked()) {                //注释2                dispatchOnceInnerLocked(&nextWakeupTime);            }            // 注释3            if (runCommandsLockedInterruptible()) {                nextWakeupTime = LONG_LONG_MIN;            }            const nsecs_t nextAnrCheck = processAnrsLocked();            nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);            // We are about to enter an infinitely long sleep, because we have no commands or            // pending or queued events            if (nextWakeupTime == LONG_LONG_MAX) {                mDispatcherEnteredIdle.notify_all();            }        } // release lock        nsecs_t currentTime = now();        int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);        mLooper->pollOnce(timeoutMillis);    }
复制代码


首先判断是否有指令需要执行,如果没有,则调用注释 2 处的 dispatchOnceInnerLocked 去根据事件类型进行处理,如果有则优先调用 3 处的 runCommandsLockedInterruptible 处理事件, 为了让线程可以立即进入事件处理,将 nextWakeupTime 设置为 LONG_LONG_MIN,这样线程在指令执行完毕后可以立即被唤醒去处理输入事件。


我们先看下 2 处的事件处理


void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {        .....        // 如果 InputDispatcher 被冻结,则不进行派发操作        if (mDispatchFrozen) {            if (DEBUG_FOCUS) {                ALOGD("Dispatch frozen.  Waiting some more.");            }            return;        }        .......        // 如果没有待分发的事件,去 mInboundQueue 取出一个事件        if (!mPendingEvent) {            //如果没有待分发的事件,就去 mInboundQueue 中取出一个事件            if (mInboundQueue.empty()) {                .......                if (!mPendingEvent) {                    return;                }            } else {                //不为空,则从头部取出一个事件                mPendingEvent = mInboundQueue.front();                mInboundQueue.pop_front();                traceInboundQueueLengthLocked();            }            // Poke user activity for this event.            if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {                pokeUserActivityLocked(*mPendingEvent);            }        }
ALOG_ASSERT(mPendingEvent != nullptr); bool done = false; DropReason dropReason = DropReason::NOT_DROPPED; //1 ..... switch (mPendingEvent->type) { case EventEntry::Type::CONFIGURATION_CHANGED: ..... case EventEntry::Type::DEVICE_RESET: ..... case EventEntry::Type::FOCUS: ..... case EventEntry::Type::DRAG: ..... case EventEntry::Type::KEY:..... case EventEntry::Type::MOTION: { //2 std::shared_ptr<MotionEntry> motionEntry = std::static_pointer_cast<MotionEntry>(mPendingEvent); //如果没有及时响应窗口切换操作 if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { dropReason = DropReason::APP_SWITCH; } //事件过期 if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) { dropReason = DropReason::STALE; } //阻碍其他窗口获取事件 if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { dropReason = DropReason::BLOCKED; } //3 done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime); break; }
case EventEntry::Type::SENSOR: ..... }
if (done) { if (dropReason != DropReason::NOT_DROPPED) { dropInboundEventLocked(*mPendingEvent, dropReason); } mLastDropReason = dropReason; //释放本次事件处理对象 releasePendingEventLocked(); //使得 inputDispatcher 能够快速处理下一个分发事件 *nextWakeupTime = LONG_LONG_MIN; } }
复制代码


上面代码主要截取了 Motion 事件相关的源码,主要的事情有下面几点:


如果 InputDispatcher 被冻结,则不进行派发操作,InputDispatcher 有三种状态,分别是正常,冻结,和禁用。


  • 注释 1 代表了事件被丢弃的原因,默认值是 NOT_DROPPED ,代表不会丢弃

  • 注释 2 对事件类型进行区分,这里主要是看 Motion 事件。

  • 注释 3 最终会调用 dispatchMotionLocked 来为这个事件寻找合适的窗口。

InputChannel 注册

首先需要一定的 WMS 知识储备,我们回顾下 Window 是在 ViewRootImpl 的 setView 方法中传入 WMS 的。


public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        ...        InputChannel inputChannel = null;        if ((mWindowAttributes.inputFeatures                & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {            inputChannel = new InputChannel();        }        ...        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                getHostVisibility(), mDisplay.getDisplayId(),                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,                mAttachInfo.mOutsets, mInputChannel);        ...        if (mInputChannel != null) {            mInputEventReceiver = new WindowInputEventReceiver(inputChannel,                    Looper.myLooper());        }    }
复制代码


mWindowSession 是 IWindowSession 在 app 端的代理对象。实际执行的是 Session 类,最终会调用到 WMS 的 addWindow


public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility,                         int displayId, int requestUserId, InsetsState requestedVisibility,                         InputChannel outInputChannel...) {
... final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0); if (openInputChannels) { win.openInputChannel(outInputChannel); } ... }
复制代码


注释 1 处调用 WindowState 打开 InputChannel 通道,继续看

void openInputChannel(InputChannel outInputChannel) {    ...    String name = getName();    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1    mInputChannel = inputChannels[0];    mClientChannel = inputChannels[1];    mInputWindowHandle.inputChannel = inputChannels[0];    if (outInputChannel != null) {        mClientChannel.transferTo(outInputChannel);        mClientChannel.dispose();        mClientChannel = null;    }    ...    mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);//2}
public static InputChannel[] openInputChannelPair(String name) { ... return nativeOpenInputChannelPair(name);}
复制代码


static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env...) {    ...    sp<InputChannel> serverChannel;    sp<InputChannel> clientChannel;    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,            std::make_unique<NativeInputChannel>(serverChannel));    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,            std::make_unique<NativeInputChannel>(clientChannel));    ...    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);    return channelPair;}
复制代码


status_t InputChannel::openInputChannelPair(const String8& name,        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {    int sockets[2];    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {        ...        return result;    }    ...    outServerChannel = new InputChannel(serverChannelName, sockets[0]);    outClientChannel = new InputChannel(clientChannelName, sockets[1]);    return OK;}
复制代码


通过以上代码可以看出 InputChannel 使用的是 socket 通讯,且 WindowState 的 openInputChannel 中会根据名称返回的 inputChannels 是一个服务端和客户端的输入通道数组 其中:下标 0:表示服务端的 InputChannel 下标 1:表示客户端的 InputChannel


上面代码最后会调用 mService.mInputManager.registerInputChannel 是将 wms 在服务端的 InputChannel 注册到 IMS 中。这样在 IMS 输入系统就可以给服务端的 InputChannel 写入数据,在 WMS 的客户端 InputChannel 就可以读取数据


而调用 mClientChannel.transferTo(outInputChannel)可以将 app 端的 InputChannel 和 wms 的客户端 InputChannel 关联 这样就可以向客户端 InputChannel 中写入数据然后通知 app 端的 InputChannel,实际传递给 ViewRootImpl 对象处理,接着就是 View 层面的处理了。


继续跟进 registerInputChannel 会进入 native 侧

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);    ...    status_t status = im->registerInputChannel(            env, inputChannel, inputWindowHandle, monitor);//1}
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */, const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { return mInputManager->getDispatcher()->registerInputChannel( inputChannel, inputWindowHandle, monitor);}
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { ... sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor); int fd = inputChannel->getFd(); mConnectionsByFd.add(fd, connection); ... // registerInputChannel里面传入的monitor是false --> nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false); // 所以这个流程不会将窗口的channel放到mMonitoringChannels里面 if (monitor) { mMonitoringChannels.push(inputChannel); } ...}
复制代码


最后 InputChannel 会进入 native 侧通过 NativeInputManager 调用 InputDispatcher 并进行注册,这里传入的 inputchannel 是 WMS 在服务端 InputChannel,在 InputDispatcher 中创建一个 Connection 并加入到 mConnectionsByFd 队列中,key 为当前 inputChannel 的 fd。获取的时候也是通过 inputChannel 的 fd 去获取。


小结:WMS 和输入系统 InputDispatcher 使用的 socket 通讯,在 View 端,WMS 端以及 IMS 端都有一个 InputChannel。

确认窗口

bool InputDispatcher::dispatchMotionLocked(       nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {            ...            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);            }            ...            dispatchEventLocked(currentTime, entry, inputTargets);            return true;        }
复制代码


回到 dispatchMotionLocked 最后会调用 dispatchEventLocked,而且通过 findFocusedWindowTargetsLocked 方法来确认当前需要传递事件的窗口。

int32_t InputDispatcher::findFocusedWindowTargetsLocked(...inputTargets){    ...    addWindowTargetLocked(mFocusedWindowHandle,            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),            inputTargets);    ...}
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) { inputTargets.push(); const InputWindowInfo* windowInfo = windowHandle->getInfo(); InputTarget& target = inputTargets.editTop(); target.inputChannel = windowInfo->inputChannel; target.flags = targetFlags; target.xOffset = - windowInfo->frameLeft; target.yOffset = - windowInfo->frameTop; target.scaleFactor = windowInfo->scaleFactor; target.pointerIds = pointerIds;}
复制代码


windowInfo->inputChannel 通道赋值给了 target.inputChannel,windowInfo 是一个 mFocusedWindowHandle 对象,是一个包含焦点 Window 信息的对象。

窗口注册

public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility,                         int displayId, int requestUserId, InsetsState requestedVisibility,                         InputChannel outInputChannel...){        ...        final boolean openInputChannels = (outInputChannel != null                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);        if  (openInputChannels) {            win.openInputChannel(outInputChannel);        }        ...        if (focusChanged) {                displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,                        false /*updateInputWindows*/);        }        displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);        ...    }
复制代码


updateInputWindowsLw 经过层层调用最终会走到 InputDispatcher::setInputWindows 中,

void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {    ...    mWindowHandles = inputWindowHandles;    sp<InputWindowHandle> newFocusedWindowHandle;    ...    for (size_t i = 0; i < mWindowHandles.size(); i++) {        const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);        ...        if (windowHandle->getInfo()->hasFocus) {            newFocusedWindowHandle = windowHandle;        }        ...        mFocusedWindowHandle = newFocusedWindowHandle;    }    ...
}
复制代码


我们知道了 window 相关的信息赋值,上下关联起来了,其中

  • mWindowHandles:代表所有 Window 的 Handler 对象

  • mFocusedWindowHandle:表示焦点 Window 的 Handler 对象 通过这些代码就让我们 IMS 中获取到了需要处理的焦点 Window。

兜兜转转,我们再回到最终触发事件分发的方法

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);        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);        if (connectionIndex >= 0) {            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);        }        ...    }}
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) { ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd()); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); if (connection->inputChannel.get() == inputChannel.get()) { return connectionIndex; } } return -1;}
复制代码


dispatchEventLocked 主要作用:轮询 inputTargets,根据 inputTarget.inputChannel 获取其在 mConnectionsByFd 中的索引,根据索引获取 Connection 对象,并调用 prepareDispatchCycleLocked 进行处理。prepareDispatchCycleLocked 方法内部调用了 enqueueDispatchEntriesLocked 方法。

void InputDispatcher::enqueueDispatchEntriesLocked(connection,..){    // Enqueue dispatch entries for the requested modes.    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,...);//1    ...    // If the outbound queue was previously empty, start the dispatch cycle going.    if (wasEmpty && !connection->outboundQueue.isEmpty()) {//2        startDispatchCycleLocked(currentTime, connection);//3    }}
复制代码


enqueueDispatchEntryLocked 方法中会将输入事件重新封装为一个 DispatchEntry 并压入 connection 的 outboundQueue 队列中。然后判断如果事件不为空,则调用 startDispatchCycleLocked 循环发送输入事件。

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,        const sp<Connection>& connection) {    while (connection->status == Connection::STATUS_NORMAL            && !connection->outboundQueue.isEmpty()) {        DispatchEntry* dispatchEntry = connection->outboundQueue.head;        ...        EventEntry* eventEntry = dispatchEntry->eventEntry;        switch (eventEntry->type) {        case EventEntry::TYPE_KEY: {        }        case EventEntry::TYPE_MOTION: {        ...        status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,                motionEntry->deviceId, motionEntry->source, motionEntry->displayId,                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);        ...        }        ...        connection->outboundQueue.dequeue(dispatchEntry);        connection->waitQueue.enqueueAtTail(dispatchEntry)    }       ...}
复制代码


startDispatchCycleLocked 中通过 publishKeyEvent 会将事件写入到 WMS 传递下来的 InputChannel 通道中。这样 WMS 端的 InputChannel 就可以通过 socket 获取到数据。在发送完毕后会将事件移出 outboundQueue 队列,并放入到 waitQueue 等待队列中,等待事件处理完毕后再移出.

那什么时候知道事件处理完毕呢?

在 InputDispatcher::registerInputChannel 方法里面注册了 handleReceiveCallback 回调:

status_t InputDispatcher::registerInputChannel(...) {        ...        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);        ...}
复制代码


当 app 层的事件处理完毕之后就会回调 handleReceiveCallback

int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {        InputDispatcher * d = static_cast < InputDispatcher * > (data);        ...        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;            }        }        ...        // Unregister the channel.        d->unregisterInputChannelLocked(connection->inputChannel, notify);    }
复制代码


先调用 finishDispatchCycleLocked 去往 mCommandQueue 里面加入一个执行 doDispatchCycleFinishedLockedInterruptible 的命令。

void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(        CommandEntry* commandEntry) {    sp<Connection> connection = commandEntry->connection;    ...    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;    }    ...    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);    ...    if (dispatchEntry == connection->findWaitQueueEntry(seq)) {        connection->waitQueue.dequeue(dispatchEntry);        ...    }}
复制代码


该方法会将 connection->waitQueue 出栈,这样整个输入系统的分发过程就闭环了。

简单总结下:

  1. ViewRootImpl 在 setView 方法中创建一个 InputChannel 通道并在将 Window 添加给 WMS 的时候作为参数传递给 WMS。

  2. WMS 在添加 Window 的过程中会调用 updateInputWindows,这个方法最终会调用到 InputDispatcher::setInputWindows 中, 并给 InputDispatcher 的 Window 队列以及焦点 Window 赋值,这样 IMS 就可以找到对应的 Window 了

  3. 在 WMS 在添加 Window 的过程中还会创建一个 socketpair 通道的 InputChannel,其中客户端的 socket 与 app 层的 InputChannel 关联,用于 WMS 与 app 通讯 服务端的 socket 传递给 IMS,用于 IMS 和 WMS 通讯。

  4. 客户端在接收到输入事件后会根据当前获取焦点 Window 的 InputChannel 找到对应的 Connection 连接,这个 Connection 用于与 WMS 进行通讯,内部其实就是使用前面的 socket 通讯。

  5. 事件分发后将输入事件放入到 waitQueue 中,等待事件处理完毕后,将事件移出 waitQueue

结尾

本文将事件分发的流程从底到上梳理了一下,希望能帮助大家清楚知道硬件产生的事件是怎么从输入系统传到 app 层进行处理的,欢迎关注我学习硬核技术,感谢支持,欢迎关注、转发、评论。


微信公众号首发:欢迎关注、转发、评论

发布于: 刚刚阅读数: 4
用户头像

江湖修行

关注

还未添加个人签名 2021-12-05 加入

还未添加个人简介

评论

发布
暂无评论
Android底层事件分发机制-完结篇_android_江湖修行_InfoQ写作社区