
文章插图
InputReader的mQueuedListener其实就是InputDispatcher对象 , 所以mQueuedListener->flush()就是通知InputDispatcher事件读取完毕 , 可以派发事件了, InputDispatcherThread是一个典型Looper线程 , 基于native的Looper实现了Hanlder消息处理模型 , 如果有Input事件到来就被唤醒处理事件 , 处理完毕后继续睡眠等待 , 简化代码如下:
bool InputDispatcherThread::threadLoop() { mDispatcher->dispatchOnce(); return true;} void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; {<!--被唤醒 , 处理Input消息--> if (!haveCommandsLocked()) { dispatchOnceInnerLocked(&nextWakeupTime); } ... }nsecs_t currentTime = now(); int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); <!--睡眠等待input事件--> mLooper->pollOnce(timeoutMillis);}以上就是派发线程的模型 , dispatchOnceInnerLocked是具体的派发处理逻辑 , 这里看其中一个分支 , 触摸事件:
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { ... case EventEntry::TYPE_MOTION: { MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); ... done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; } bool InputDispatcher::dispatchMotionLocked( nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ...Vector<InputTarget> inputTargets; bool conflictingPointerActions = false; int32_t injectionResult; if (isPointerEvent) { <!--关键点1 找到目标Window--> injectionResult = findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime, &conflictingPointerActions); } else { injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); } ... <!--关键点2 派发--> dispatchEventLocked(currentTime, entry, inputTargets); return true;}【InputManagerService 十分钟让你了解Android触摸事件原理】从以上代码可以看出 , 对于触摸事件会首先通过findTouchedWindowTargetsLocked找到目标Window , 进而通过dispatchEventLocked将消息发送到目标窗口 , 下面看一下如何找到目标窗口 , 以及这个窗口列表是如何维护的 。
如何为触摸事件找到目标窗口Android系统能够同时支持多块屏幕 , 每块屏幕被抽象成一个DisplayContent对象 , 内部维护一个WindowList列表对象 , 用来记录当前屏幕中的所有窗口 , 包括状态栏、导航栏、应用窗口、子窗口等 。对于触摸事件 , 我们比较关心可见窗口 , 用adb shell dumpsys SurfaceFlinger看一下可见窗口的组织形式:

文章插图
那么 , 如何找到触摸事件对应的窗口呢 , 是状态栏、导航栏还是应用窗口呢 , 这个时候DisplayContent的WindowList就发挥作用了 , DisplayContent握着所有窗口的信息 , 因此 , 可以根据触摸事件的位置及窗口的属性来确定将事件发送到哪个窗口 , 当然其中的细节比一句话复杂的多 , 跟窗口的状态、透明、分屏等信息都有关系 , 下面简单瞅一眼 , 达到主观理解的流程就可以了 ,
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { ... sp<InputWindowHandle> newTouchedWindowHandle; bool isTouchModal = false; <!--遍历所有窗口--> size_t numwindows = mWindowHandles.size(); for (size_t i = 0; i < numWindows; i++) { sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId != displayId) { continue; // wrong display } int32_t flags = windowInfo->layoutParamsFlags; if (windowInfo->visible) { if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0; <!--找到目标窗口--> if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { newTouchedWindowHandle = windowHandle; break; // found touched window, exit window loop } } ...mWindowHandles代表着所有窗口 , findTouchedWindowTargetsLocked的就是从mWindowHandles中找到目标窗口 , 规则太复杂 , 总之就是根据点击位置更窗口Z order之类的特性去确定 , 有兴趣可以自行分析 。不过这里需要关心的是mWindowHandles , 它就是是怎么来的 , 另外窗口增删的时候如何保持最新的呢?这里就牵扯到跟WindowManagerService交互的问题了 , mWindowHandles的值是在InputDispatcher::setInputWindows中设置的 ,
推荐阅读
- 多线程同步的五种方法,让你全面了解线程同步
- 数据库JSON怎么改?一个SQL函数让你的操作瞬间高大上
- 早餐注意这几点,让你一天都活力满满!
- 发动机是汽车的灵魂,汽车发动机高清分解图,让你看懂发动机
- 10种方式让你的跑步丰富起来
- 白茶让你轻松做白美人
- 九款茶让你告别便秘 调理肠胃
- 现今日本的黑茶让你喝出好身材
- 茶让你从发丝到脚趾每处得到美化
- 黑茶十问十答,让你全面了解黑茶
