一:概述
由前一篇文章Android输入系统之InputReader(AndroidV)-优快云博客可知,当InputReader读取到input事件后,会唤醒InputDispatcher线程来进行事件分发。下面来看一下InputDispatcher是如何进行事件分发的。
二:input事件分发
2.1 InputDispatcher.dispatchOnce
当InputDispatcher被唤醒后,会执行dispatchOnce来开始分发事件
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LLONG_MAX;
{ // acquire lock
...
if (!haveCommandsLocked()) {
//分发事件
dispatchOnceInnerLocked(/*byref*/ nextWakeupTime);
}
...
//判断是否有anr发生
const nsecs_t nextAnrCheck = processAnrsLocked();
...
} // release lock
...
//进入epoll_wait
mLooper->pollOnce(timeoutMillis);
}
2.2 InputDispatcher.dispatchOnceInnerLocked
- 判断事件分发是否被冻结,如果冻结则拦截事件,不进行分发
- 从mInboundQueue队列中取出第一个事件,赋值给mPendingEvent
- 根据事件类型(motion、key等),分别调用不同的方法继续input事件分发
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) {
...
//判断事件分发是否被冻结(例如应用启动过程中,点击应用区域是不会有事件上报的,在这里被拦截了)
if (mDispatchFrozen) {
return;
}
...
//有待处理的事件
if (!mPendingEvent) {
if (mInboundQueue.empty()) {
...
} else {
//取出mInboundQueue队列中的第一个事件,赋值给mPendingEvent
mPendingEvent = mInboundQueue.front();
//删除队列中的第一个事件
mInboundQueue.pop_front();
...
}
...
}
...
DropReason dropReason = DropReason::NOT_DROPPED;
...
//判断将要分发的事件类型
switch (mPendingEvent->type) {
...
case EventEntry::Type::MOTION: {
std::shared_ptr<const MotionEntry> motionEntry =
std::static_pointer_cast<const MotionEntry>(mPendingEvent);
//过时事件
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
...
}
...
//分发事件
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
break;
}
...
}
...
if (done) {
//事件需要被丢弃
if (dropReason != DropReason::NOT_DROPPED) {
dropInboundEventLocked(*mPendingEvent, dropReason);
}
...
//释放pending事件
releasePendingEventLocked();
...
}
}
2.3 InputDispatcher.dispatchMotionLocked
motion事件会调用dispatchMotionLocked函数来继续事件分发流程。通过findTouchedWindowTargetsLocked找到touch事件对应的window,再调用dispatchEventLocked继续分发事件
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
bool InputDispatcher::dispatchMotionLocked(...) {
...
//丢弃所有需要丢弃的事件,停止事件分发
if (*dropReason != DropReason::NOT_DROPPED) {
...
return true;
}
const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER);
...
if (isPointerEvent) {
if (mDragState &&
(entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) {
//如果是拖拽事件,阻止down事件下发直到拖拽结束
pilferPointersLocked(mDragState->dragWindow->getToken());
}
//寻找touch事件对应的window
inputTargets =
findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult);
} else {
...
}
//分发motion事件
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
2.4 InputDispatcher.findTouchedWindowTargetsLocked
- 通过displayId/x/y/isStylus找到motion事件对应的window,如果没有则把第一个前台窗口当做对应窗口
- 窗口有效性的判定:是否处于可接收事件的状态;是否可见;是否处于displayId对应的display中;事件的x/y坐标是否在window可点击坐标范围内等
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry,
InputEventInjectionResult& outInjectionResult) {
...
std::vector<InputTarget> targets;
//获取touch事件对应的displayId
const ui::LogicalDisplayId displayId = entry.displayId;
...
TouchState tempTouchState;
...
bool isSplit = shouldSplitTouch(tempTouchState, entry);
//判断是否是悬停action
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
//判断是否是down事件
const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
(maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
//判断是否是新手势
const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
...
//如果是新手势,把isSplit置为false
if (newGesture) {
isSplit = false;
}
...
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
//通过entry找到此次touch事件的x/y坐标
const auto [x, y] = resolveTouchedPosition(entry);
...

最低0.47元/天 解锁文章
604

被折叠的 条评论
为什么被折叠?



