前序
书接上文,前面我们讲了下 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 层进行处理的,欢迎关注我学习硬核技术,感谢支持,欢迎关注、转发、评论。
微信公众号首发:欢迎关注、转发、评论
评论