前序
书接上文,前面我们讲了下 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 相关的信息赋值,上下关联起来了,其中
兜兜转转,我们再回到最终触发事件分发的方法
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 出栈,这样整个输入系统的分发过程就闭环了。
简单总结下:
ViewRootImpl 在 setView 方法中创建一个 InputChannel 通道并在将 Window 添加给 WMS 的时候作为参数传递给 WMS。
WMS 在添加 Window 的过程中会调用 updateInputWindows,这个方法最终会调用到 InputDispatcher::setInputWindows 中, 并给 InputDispatcher 的 Window 队列以及焦点 Window 赋值,这样 IMS 就可以找到对应的 Window 了
在 WMS 在添加 Window 的过程中还会创建一个 socketpair 通道的 InputChannel,其中客户端的 socket 与 app 层的 InputChannel 关联,用于 WMS 与 app 通讯 服务端的 socket 传递给 IMS,用于 IMS 和 WMS 通讯。
客户端在接收到输入事件后会根据当前获取焦点 Window 的 InputChannel 找到对应的 Connection 连接,这个 Connection 用于与 WMS 进行通讯,内部其实就是使用前面的 socket 通讯。
事件分发后将输入事件放入到 waitQueue 中,等待事件处理完毕后,将事件移出 waitQueue
结尾
本文将事件分发的流程从底到上梳理了一下,希望能帮助大家清楚知道硬件产生的事件是怎么从输入系统传到 app 层进行处理的,欢迎关注我学习硬核技术,感谢支持,欢迎关注、转发、评论。
微信公众号首发:欢迎关注、转发、评论
评论