前序
书接上文,前面我们讲了下Android输入事件的全貌,从底层硬件产生事件,经过IMS的InputReader加工转换再经过InputDspatcher分发转给WMS中间层,再通过WMS定位到准确的window进行我们熟悉的上层事件处理。
上文我们详细讲解了IMS的初始化流程及底层事件的拦截处理流程,今天我们继续讲解InputReader处理完的事件怎么进行分发的。
InputDspatcher 分发事件
将封装的事件压入到队列中并唤醒 InputDispatchert 线程进行分发处理。
InputDispatcher线程唤醒
kotlin
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
scss
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处的事件处理
php
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的。
ini
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
java
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通道,继续看
ini
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);
}
ini
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;
}
ini
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侧
scss
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。
确认窗口
scss
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方法来确认当前需要传递事件的窗口。
ini
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信息的对象。
窗口注册
java
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中,
ini
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。
兜兜转转,我们再回到最终触发事件分发的方法
ini
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方法。
scss
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循环发送输入事件。
rust
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回调:
arduino
status_t InputDispatcher::registerInputChannel(...) {
...
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
...
}
当app层的事件处理完毕之后就会回调handleReceiveCallback
arduino
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的命令。
ini
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层进行处理的,欢迎关注我学习硬核技术,感谢支持,欢迎关注、转发、评论。