当前位置: 首页 > news >正文

视差 长沙做网站阿城区建设小学网站

视差 长沙做网站,阿城区建设小学网站,网站风格有哪些,简网app工场官网免费Framework层之输入系统 第一篇 深入Android S (12.0) 探索Framework之输入系统IMS的构成与启动 第二篇 深入Android S (12.0) 探索Framework之输入子系统InputReader的流程 第三篇 深入Android S (12.0) 探索Framework之输入子系统InputDispatcher的流程 文章目录 Framework层…Framework层之输入系统 第一篇 深入Android S (12.0) 探索Framework之输入系统IMS的构成与启动 第二篇 深入Android S (12.0) 探索Framework之输入子系统InputReader的流程 第三篇 深入Android S (12.0) 探索Framework之输入子系统InputDispatcher的流程 文章目录 Framework层之输入系统前言一、InputDispatcher 前期准备1.InputManager2.InputDispatcher3.InputDispatcher::start()4.InputThread5.InputDispacher::dispatchOnce()6.InputDispacher::dispatchOnceInnerLocked()小结 二、InputDispatcher 分发 Motion 事件1.InputDispacher::dispatchMotionLocked()2.InputDispacher::findTouchedWindowTargetsLocked()2.1 TouchState2.2 InputDispatcher::findTouchedWindowAtLocked()2.3 TouchState::addOrUpdateWindow()2.4 InputDispatcher::addWindowTargetLocked() 3.InputDispacher::dispatchEventLocked()4.InputDispacher::prepareDispatchCycleLocked()5.InputDispacher::enqueueDispatchEntriesLocked()6.InputDispacher::startDispatchCycleLocked()7.InputPublisher::publishMotionEvent()8.InputChannel::sendMessage()9.NativeInputEventReceiver::handleEvent()10.NativeInputEventReceiver::consumeEvents()11.InputEventReceiver#dispatchInputEvent()12.ViewRootImpl#WindowInputEventReceiver#onInputEvent()13.ViewRootImpl#enqueueInputEvent()14.ViewRootImpl#doProcessInputEvents()15.ViewRootImpl#deliverInputEvent()小结 总结 前言 上一篇文章深入探索了 Android Framework 的输入子系统 InputReader 的工作流程在 InputReader 的一次线程循环中通过 EventHub::getEvent() 函数尽可能多地读取设备增删事件与原始输入事件并将它们封装成 RawEvent 结构体存入缓存 buffer 中供 InputReader 进行处理。InputReader 通过调用其 processEventsLocked() 函数对获取事件进行分类处理对于设备节点事件将根据设备的可用性来加载或移除设备对应的配置信息。我们重点关注原始输入事件InputReader 对其进行转译、封装与加工后将结果暂存到 mQueuedListener 中。最后调用 QueuedInputListener::flush() 函数将所有暂存、已加工过的输入事件交付给 InputDispatcher 来进行分发。本篇将深入探索 InputDispatcher 的工作流程它是如何来分发这些输入事件的 一、InputDispatcher 前期准备 InputDispatcher 是 IMS 中的一个关键组件运行于一个独立的线程中在 InputDispatcher 中保管了来自 WindowManagerService 的所有窗口的信息。在一次线程循环中会获取位于派发队列队首位置的事件然后在其保管的窗口中寻找合适的窗口并将事件派发给此窗口。 1.InputManager xref: /frameworks/native/services/inputflinger/InputManager.h class InputManager : public InputManagerInterface, public BnInputFlinger { protected:~InputManager() override;public:InputManager(const spInputReaderPolicyInterface readerPolicy,const spInputDispatcherPolicyInterface dispatcherPolicy);...... private:spInputReaderInterface mReader;spInputClassifierInterface mClassifier;spInputDispatcherInterface mDispatcher; };xref: /frameworks/native/services/inputflinger/InputManager.cpp InputManager::InputManager(const spInputReaderPolicyInterface readerPolicy,const spInputDispatcherPolicyInterface dispatcherPolicy) {// 创建 InputDispatcher 对象使用 InputDispatcherPolicyInterface 接口用于对事件进行分发mDispatcher createInputDispatcher(dispatcherPolicy);// 创建 InputClassifier 对象使用 InputListenerInterface用于对事件分类mClassifier new InputClassifier(mDispatcher);// 创建 InputReader 对象使用 InputReaderPolicyInterface 和 InputListenerInterface// 其通过 EventHub 监听/dev/input事件获取事件然后把事件加工后发送给 InputClassfiermReader createInputReader(readerPolicy, mClassifier); }由第一篇文章的分析可知InputDispatcher 的实例对象是在构建 InputManager 实例对象时在其构造方法中通过调用工厂方法 createInputDispatcher() 传入 InputDispatcherPolicyInterface 接口的实现类来创建的在该工厂方法内部直接新建 InputDispatcher 对象。继续看一下 InputDispatcher 类的声明及构造函数 2.InputDispatcher xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.h class InputDispatcher : public android::InputDispatcherInterface { protected:~InputDispatcher() override; public:explicit InputDispatcher(const spInputDispatcherPolicyInterface policy); ...... private:std::unique_ptrInputThread mThread;spInputDispatcherPolicyInterface mPolicy;spLooper mLooper;spInputReporterInterface mReporter;std::shared_ptrEventEntry mPendingEvent GUARDED_BY(mLock);std::dequestd::shared_ptrEventEntry mInboundQueue GUARDED_BY(mLock);std::dequestd::shared_ptrEventEntry mRecentQueue GUARDED_BY(mLock);std::dequestd::unique_ptrCommandEntry mCommandQueue GUARDED_BY(mLock); };xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp InputDispatcher::InputDispatcher(const spInputDispatcherPolicyInterface policy): mPolicy(policy),......mDispatchEnabled(false),mDispatchFrozen(false),mInputFilterEnabled(false),// mInTouchMode将由WindowManager初始化为默认设备配置。为了避免在调用从未出现的情况下泄漏堆栈// 并且为了测试无论如何都要在这里初始化它。mInTouchMode(true),......mLatencyTracker(mLatencyAggregator),mCompatService(getCompatService()) {mLooper new Looper(false); // 新建自己的 Looper 对象mReporter createInputReporter(); // 新建 InputReporter 对象mKeyRepeatState.lastKeyEntry nullptr;policy-getDispatcherConfiguration(mConfig); }InputDispatcher 实现了 InputDispatcherInterface 接口在分析输入子系统 InputReader 时在其线程循环的最后QueueInputListener 调用此接口将 InputReader 读取并处理过的事件以 NotifyArgs 结构体的形式提交给 InputDispatcher。 3.InputDispatcher::start() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp status_t InputDispatcher::start() {if (mThread) {return ALREADY_EXISTS;}mThread std::make_uniqueInputThread(InputDispatcher, [this]() { dispatchOnce(); }, [this]() { mLooper-wake(); });return OK; }InputDispatcher 实例对象创建完毕后处于待命状态等到 IMS # start() 函数调用后启动输入系统时调用 InputDispatcher::start() 方法来启动承载 InputDispatcher 的线程。但在方法内没有看到启动线程的代码逻辑只是通过 std::make_unique 函数来构建 InputThread 的实例对象那就具体来看一下 InputThread 类。 4.InputThread xref: /frameworks/native/services/inputflinger/include/InputThread.h class InputThread { public:explicit InputThread(std::string name, std::functionvoid() loop,std::functionvoid() wake nullptr);virtual ~InputThread();bool isCallingThread(); private:std::string mName; // 线程名std::functionvoid() mThreadWake;spThread mThread; // 承载 InputDispatcher/InputReader 运行的线程 };xref: /frameworks/native/services/inputflinger/InputThread.cpp class InputThreadImpl : public Thread { public: // explicit 关键字的作用就是防止类构造函数的隐式自动转换且只对有一个参数的类构造函数有效explicit InputThreadImpl(std::functionvoid() loop): Thread(/* canCallJava */ true), mThreadLoop(loop) {}~InputThreadImpl() {}private:std::functionvoid() mThreadLoop; // 存储一个可调用对象这里指的是 lambda 表达式bool threadLoop() override {mThreadLoop();return true;} };InputThread::InputThread(std::string name, std::functionvoid() loop, std::functionvoid() wake): mName(name), mThreadWake(wake) {// 使用封装的可调用对象 loop 新建 InputThreadImpl 对象mThread new InputThreadImpl(loop);// 启动 InputThreadImpl 线程mThread-run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY); }InputThread 类本身不是一个线程其内部是通过 InputThreadImpl 类来实现线程的具体功能。InputThreadImpl 类继承自 Thread 类而 C 中的 Thread 类有一个名为 threadLoop() 的纯虚函数当线程开始运行后将会在内建的线程循环中不断地调用 threadLoop() 函数直到此函数返回 false则退出线程循环结束线程。 但从 InputThreadImpl 类的定义可以看出threadLoop() 函数会一直保持循环(因为返回值始终为 true)且每一次循环会调用一次 mThreadLoop() 函数。而 mThreadLoop() 函数就是在启动 InputDispacher 时构建 InputThread 实例对象传入的封装好的可调用函数对象 InputDispacher::dispatchOnce() 函数。也就是在 InputDispatcher 启动时会创建一个线程然后不断循环调用 InputDispacher::dispatchOnce() 函数。 5.InputDispacher::dispatchOnce() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime LONG_LONG_MAX;{ // 获取锁std::scoped_lock _l(mLock); // 互斥锁mutex与条件变量结合使用确保线程间的安全访问共享资源。// std::condition_variable mDispatcherIsAlive条件变量用于在多线程程序中实现线程间的同步和互斥。mDispatcherIsAlive.notify_all(); // 唤醒与该条件变量关联的所有等待事件// 如果 mCommandQueue 中没有待处理的命令 Command则执行 dispatchOnceInnerLocked() 函数进行事件派发if (!haveCommandsLocked()) {// 调用 dispatchOnceInnerLocked() 函数将事件派发给合适的 Window// 其中的传出参数 nextWakeupTime 决定了下次派发线程循环的执行时间dispatchOnceInnerLocked(nextWakeupTime);}// 如果 mCommandQueue 中有待处理的 Command则循环从中取出并执行所有的Command// 如果执行了任何 Command将下次唤醒时间设置为最小值并强制下一个poll立即唤醒。if (runCommandsLockedInterruptible()) {nextWakeupTime LONG_LONG_MIN;}// 如果此时正在等待派发出去的事件的 ack(目标应用的响应)则要更早地唤醒以检查目标应用是否正在 ANRconst nsecs_t nextAnrCheck processAnrsLocked(); // 处理ANR获取下次checkANR的时间// 获取下次唤醒和下次检测ANR之间较小的时间nextWakeupTime std::min(nextWakeupTime, nextAnrCheck); // 如果唤醒时间还是 LONG_LONG_MAX 没有被修改表示没有待处理的 Command、挂起或排队的事件那么将进入无限期的休眠中if (nextWakeupTime LONG_LONG_MAX) {mDispatcherEnteredIdle.notify_all();}} // 释放锁nsecs_t currentTime now(); // 获取当前时间计算 Looper 的睡眠等待时间int timeoutMillis toMillisecondTimeoutDelay(currentTime, nextWakeupTime);// 调用 pollOnce 函数进入阻塞等待回调、超时或唤醒mLooper-pollOnce(timeoutMillis); }condition_variable条件变量是 C11 标准库中的一种同步原语头文件condition_variable用于在多线程程序中实现线程间的同步和互斥。它允许一个或多个线程等待另一个线程发出信号或通知以避免忙等待的情况从而提高程序的效率。在使用条件变量时通常需要与互斥锁mutex结合使用以确保线程间的安全访问共享资源。 用法当某个线程需要等待某个条件成立时它可以调用条件变量的 wait() 函数来等待同时释放它所持有的互斥锁直到另一个线程调用条件变量的 notify_one() 或 notify_all() 函数来通知等待的线程条件已经成立等待的线程才会被唤醒并重新获得互斥锁。 并将下次唤醒时间设置为最小值强制下一次poll唤醒线程 在一次线程循环中InputDispacher::dispatchOnce() 函数的执行流程如下 如果 mCommandQueue 中有待处理的 Command 命令则循环从中取出并执行所有的 Command 命令并且如果执行了任一 Command则将下次唤醒时间设置为最小值并强制下一次 poll 立即唤醒线程如果 mCommandQueue 中没有待处理的 Command 命令则调用 InputDispatcher::dispatchOnceInnerLocked() 函数派发事件如果此时正在等待派发出去的事件的 ack(目标应用的响应)则需要更早地唤醒以检查目标应用是否正在发生 ANR如果唤醒时间还是 LONG_LONG_MAX 没有被修改表示没有待处理的 Command、挂起或排队的事件那么将进入无限期的休眠中。事件分发完后调用 Looper::pollOnce 将当前的分发线程挂起等到后续 InputReader 的读取线程将新的事件发送给 InputDispacher 并唤醒其分发线程。 Command 实际指向一个函数 std::functionvoid()执行一些必要特殊的操作通知 Java 层的 InputMangerService比如焦点改变sendFocusChangedCommandLocked()ANR 发生onAnrLocked()等等。 6.InputDispacher::dispatchOnceInnerLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {nsecs_t currentTime now();// 如果设备处于非交互状态当正常调度挂起时需重置按键重复计时器。以确保设备刚脱离睡眠时终止按键重复计时器if (!mDispatchEnabled) {resetKeyRepeatLocked();}......// 优化应用程序切换的延迟当按下类似HOME/ENDCALL键时启动一个短暂超时机制(0.5s)当timeout时会立即分发事件并抛弃其他挂起的事件bool isAppSwitchDue mAppSwitchDueTime currentTime;if (mAppSwitchDueTime *nextWakeupTime) {*nextWakeupTime mAppSwitchDueTime;}// Ready to start a new event. 准备开始一个新的 event如果没有待处理的 event则抓取一个if (!mPendingEvent) { // 正常一次分发前mPendingEvent nullptrif (mInboundQueue.empty()) { //当 InputReader 向队列中插入一个输入事件后此处 mInboundQueue 就不为空......} else {// 从派发队列中将位于队首的一条 EventEntry 取出并保存在 mPendingEvent 成员变量中// mPendingEvent 表示处于派发过程中的一个输入事件。之所以使用一个成员变量而不是局部变量保存它// 是由于此次线程循环有可能不能完成此事件的派发mPendingEvent mInboundQueue.front();mInboundQueue.pop_front();traceInboundQueueLengthLocked();}......}// 现在有一个事件要调度且所有事件都是以这种方式出队并进行处理即使我们打算丢弃它们......switch (mPendingEvent-type) {......case EventEntry::Type::FOCUS: {std::shared_ptrFocusEntry typedEntry std::static_pointer_castFocusEntry(mPendingEvent);// 根据事件类型调用相应函数处理 -- 分发焦点事件dispatchFocusLocked(currentTime, typedEntry);done true;dropReason DropReason::NOT_DROPPED; // 焦点事件永远不会被丢弃break;}......case EventEntry::Type::KEY: {std::shared_ptrKeyEntry keyEntry std::static_pointer_castKeyEntry(mPendingEvent);......// 根据事件类型调用相应函数处理 -- 分发 Key 事件done dispatchKeyLocked(currentTime, keyEntry, dropReason, nextWakeupTime);break;}case EventEntry::Type::MOTION: {std::shared_ptrMotionEntry motionEntry std::static_pointer_castMotionEntry(mPendingEvent);......// 根据事件类型调用相应函数处理 -- 分发 Motion 事件// 执行 dispatchMotionLocked() 进行 Motion 事件的派发。如果派发完成无论是成功派发还是事件被丢弃都返回true// 否则返回 false以便在下次循环时再次尝试此事件的派发done dispatchMotionLocked(currentTime, motionEntry, dropReason, nextWakeupTime);break;}......}if (done) {if (dropReason ! DropReason::NOT_DROPPED) {dropInboundEventLocked(*mPendingEvent, dropReason);}mLastDropReason dropReason;// 将 mPendingEvent 设置为 nullptr使之在下次循环时可以处理派发队列中的下一条事件releasePendingEventLcked();// 强制下一个 poll 中立即唤醒 inputDispatcher 线程来干活如果此时派发队列为空// 下次循环调用此函数时会保持 nextWakeupTime 为 LONG_LONG_MAX 并直接返回使得派发线程进入无限期休眠*nextWakeupTime LONG_LONG_MIN; // force next poll to wake up immediately} }InputDispatcher::dispatchOnceInnerLocked 函数的执行流程 如果待分发事件队列 mInboundQueue 为空则会使派发线程陷入无限期休眠状态InputReader 读取线程将事件封装成新的 MotionEntry 对象并调用 InputDispatcher::enqueueInboundEventLocked() 函数向 InputDispatcher.mInboundQueue 加入了当前要处理的输入事件。如果当前没有待分发的事件那就从 InputDispatcher.mInboundQueue 取出一个 EventEntry 类型的事件赋值给 mPendingEvent。注意MotionEntry 是 EventEntry 的子类型判断 mPendingEvent 的类型如果是 Key 事件则调用 InputDispatcher.dispatchKeyLocked() 函数处理如果是 Motion 事件则调用 InputDispatcher.dispatchMotionLocked() 函数处理当然还有其他类型的事件不再逐一类举。 需要注意的是 分发一个事件至少需要一次线程循环才能完成根据分发函数的返回值来决定是否在下次循环继续尝试此事件的分发事件的分发是串行的在排队首的事件完成分发或被丢弃之前不会对后续的事件进行分发 小结 InputDispatcher 在一次线程循环中通过 InputDispatcher::dispatchOnce() 函数将 InputReader 读取线程获取的输入事件分发完后会调用 Looper::pollOnce() 将当前分发线程挂起等待 InputReader 的读取线程将新的事件发送过来并再次唤醒 InputDispatcher 的分发线程。 InputDispatcher::dispatchOnce() 函数将事件分发转交给 InputDispatcher::dispatchOnceInnerLocked() 函数进行分发首先从 InputDispatcher.mInboundQueue 取出一个 EventEntry 类型的事件然后根据事件的类型调用相应函数处理。承接上一篇文章接下来以 Motion 事件的分发为例进行分析。 二、InputDispatcher 分发 Motion 事件 1.InputDispacher::dispatchMotionLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptrMotionEntry entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {ATRACE_CALL();// Preprocessing.if (!entry-dispatchInProgress) {entry-dispatchInProgress true;logOutboundMotionDetails(dispatchMotion - , *entry);}// 对于那些不幸被丢弃的事件直接返回if (*dropReason ! DropReason::NOT_DROPPED) {setInjectionResult(*entry,*dropReason DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED: InputEventInjectionResult::FAILED);return true;}bool isPointerEvent entry-source AINPUT_SOURCE_CLASS_POINTER;// Identify targets. 确定目标-- 初始化 InputTarget 队列存放 findTouchedWindowTargetsLocked() 函数获取的目标窗口std::vectorInputTarget inputTargets;bool conflictingPointerActions false;InputEventInjectionResult injectionResult;// 根据 Motion 事件的类型寻找合适的目标窗口// 其返回值 injectionResult 指明寻找结果而找到的合适的目标窗口信息将被保存在 inputTargets 队列中if (isPointerEvent) { // 对于基于坐标点形式的事件如触摸屏点击等将根据坐标点、窗口ZOrder与区域寻找目标窗口injectionResult findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,conflictingPointerActions);} else { // 对于其他类型的 Motion 事件(例如轨迹球)将以拥有焦点的窗口作为目标injectionResult findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);}// 返回值 PENDING 表明找到了一个窗口不过如果窗口处于无响应状态则返回 false// 也就是说这个事件尚未派发完成将在下次派发线程的循环中再次尝试派发if (injectionResult InputEventInjectionResult::PENDING) {return false;}setInjectionResult(*entry, injectionResult);if (injectionResult InputEventInjectionResult::PERMISSION_DENIED) {ALOGW(Permission denied, dropping the motion (isPointer%s), toString(isPointerEvent));return true;}// 返回值不为 SUCCEEDED表明无法为此事件找到合适的窗口例如没有窗口处于焦点状态// 或点击的位置没能落在任何一个窗口内这个事件将被直接丢弃if (injectionResult ! InputEventInjectionResult::SUCCEEDED) {CancelationOptions::Mode mode(isPointerEvent? CancelationOptions::CANCEL_POINTER_EVENTS: CancelationOptions::CANCEL_NON_POINTER_EVENTS);CancelationOptions options(mode, input event injection failed);synthesizeCancelationEventsForMonitorsLocked(options);return true;}// Add monitor channels from events or focused display.addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));if (isPointerEvent) {std::unordered_mapint32_t, TouchState::iterator it mTouchStatesByDisplay.find(entry-displayId);if (it ! mTouchStatesByDisplay.end()) {const TouchState state it-second;if (!state.portalWindows.empty()) {// The event has gone through these portal windows, so we add monitoring targets of// the corresponding displays as well.for (size_t i 0; i state.portalWindows.size(); i) {const InputWindowInfo* windowInfo state.portalWindows[i]-getInfo();addGlobalMonitoringTargetsLocked(inputTargets, windowInfo-portalToDisplayId,-windowInfo-frameLeft, -windowInfo-frameTop);}}}}// Dispatch the motion.if (conflictingPointerActions) {CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,conflicting pointer actions);synthesizeCancelationEventsForAllConnectionsLocked(options);}// dispatchEventLocked() 函数继续事件的分发流程dispatchEventLocked(currentTime, entry, inputTargets);return true; }InputDispatcher::dispatchMotionLocked() 函数的核心流程如下 初始化 std::vectorInputTarget 队列用来存放 InputDispatcher::findTouchedWindowTargetsLocked() 函数获取的目标窗口如果是触摸事件调用 InputDispatcher::findTouchedWindowTargetsLocked() 函数获取目标窗口存放到 InputTarget 队列中找到目标窗口后调用 InputDispatcher::dispatchEventLocked() 函数继续事件的分发流程。 2.InputDispacher::findTouchedWindowTargetsLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry entry, std::vectorInputTarget inputTargets,nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {ATRACE_CALL();enum InjectionPermission {INJECTION_PERMISSION_UNKNOWN,INJECTION_PERMISSION_GRANTED,INJECTION_PERMISSION_DENIED};// For security reasons, we defer updating the touch state until we are sure that// event injection will be allowed.int32_t displayId entry.displayId;int32_t action entry.action;int32_t maskedAction action AMOTION_EVENT_ACTION_MASK;// 根据触摸事件的属性更新触摸状态InputEventInjectionResult injectionResult InputEventInjectionResult::PENDING;InjectionPermission injectionPermission INJECTION_PERMISSION_UNKNOWN;spInputWindowHandle newHoverWindowHandle(mLastHoverWindowHandle);spInputWindowHandle newTouchedWindowHandle;// 将当前触摸状态复制到 tempTouchState此状态将用于在此功能结束时更新 mTouchStatesByDisplay// 如果不存在指定显示的状态那么初始状态将为空const TouchState* oldState nullptr;TouchState tempTouchState;std::unordered_mapint32_t, TouchState::iterator oldStateIt mTouchStatesByDisplay.find(displayId);if (oldStateIt ! mTouchStatesByDisplay.end()) { // 获取上一次查找的结果 oldStateItoldState (oldStateIt-second);tempTouchState.copyFrom(*oldState);}bool isSplit tempTouchState.split;bool switchedDevice tempTouchState.deviceId 0 tempTouchState.displayId 0 (tempTouchState.deviceId ! entry.deviceId || tempTouchState.source ! entry.source ||tempTouchState.displayId ! displayId);bool isHoverAction (maskedAction AMOTION_EVENT_ACTION_HOVER_MOVE ||maskedAction AMOTION_EVENT_ACTION_HOVER_ENTER ||maskedAction AMOTION_EVENT_ACTION_HOVER_EXIT);bool newGesture (maskedAction AMOTION_EVENT_ACTION_DOWN ||maskedAction AMOTION_EVENT_ACTION_SCROLL || isHoverAction);const bool isFromMouse entry.source AINPUT_SOURCE_MOUSE;bool wrongDevice false;if (newGesture) {bool down maskedAction AMOTION_EVENT_ACTION_DOWN;......// 如果当前事件是这一系列事件的起点如ACTION_DOWN那本次降不会复用上一次的查找结果且会清除、重置 tempTouchState 的状态tempTouchState.reset();tempTouchState.down down;tempTouchState.deviceId entry.deviceId;tempTouchState.source entry.source;tempTouchState.displayId displayId;isSplit false;} else if (switchedDevice maskedAction AMOTION_EVENT_ACTION_MOVE) {......}if (newGesture || (isSplit maskedAction AMOTION_EVENT_ACTION_POINTER_DOWN)) {/* Case 1: New splittable pointer going down, or need target for hover or scroll. */int32_t x;int32_t y;int32_t pointerIndex getMotionEventActionPointerIndex(action);// Always dispatch mouse events to cursor position.if (isFromMouse) {x int32_t(entry.xCursorPosition);y int32_t(entry.yCursorPosition);} else {x int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));y int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));}bool isDown maskedAction AMOTION_EVENT_ACTION_DOWN;// InputDispatcher.findTouchedWindowAtLocked() 函数寻找可接收触摸事件的窗口newTouchedWindowHandle findTouchedWindowAtLocked(displayId, x, y, tempTouchState,isDown /*addOutsideTargets*/, true /*addPortalWindows*/);...... // 检验 newTouchedWindowHandle 的有效性if (newTouchedWindowHandle ! nullptr) { // 添加窗口 Flag 标志位// FLAG_DISPATCH_AS_IS声明事件应该按原样发送除非事件被转化int32_t targetFlags InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;if (isSplit) {targetFlags | InputTarget::FLAG_SPLIT;}if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {targetFlags | InputTarget::FLAG_WINDOW_IS_OBSCURED;} else if (isWindowObscuredLocked(newTouchedWindowHandle)) {targetFlags | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;}// Update hover state.......// Update the temporary touch state.BitSet32 pointerIds;......tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);}tempTouchState.addGestureMonitors(newGestureMonitors);} else {/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */// If the pointer is not currently down, then ignore the event.......// Check whether touches should slip outside of the current foreground window.// 寻找触摸窗口——非DOWN事件的处理if (maskedAction AMOTION_EVENT_ACTION_MOVE entry.pointerCount 1 tempTouchState.isSlippery()) {int32_t x int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));int32_t y int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));spInputWindowHandle oldTouchedWindowHandle tempTouchState.getFirstForegroundWindowHandle();newTouchedWindowHandle findTouchedWindowAtLocked(displayId, x, y, tempTouchState);if (oldTouchedWindowHandle ! newTouchedWindowHandle oldTouchedWindowHandle ! nullptr newTouchedWindowHandle ! nullptr) {if (DEBUG_FOCUS) {ALOGD(Touch is slipping out of window %s into window %s in display % PRId32,oldTouchedWindowHandle-getName().c_str(),newTouchedWindowHandle-getName().c_str(), displayId);}// Make a slippery exit from the old window.tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,BitSet32(0));// Make a slippery entrance into the new window.if (newTouchedWindowHandle-getInfo()-supportsSplitTouch()) {isSplit true;}// FLAG_DISPATCH_AS_SLIPPERY_ENTER声明应将事件作为初始的 DOWN 事件进行调度// 用于在触摸滑入新窗口时将 ACTION_MOVE 转换为 ACTION_DOWNint32_t targetFlags InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;if (isSplit) {targetFlags | InputTarget::FLAG_SPLIT;}if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {targetFlags | InputTarget::FLAG_WINDOW_IS_OBSCURED;} else if (isWindowObscuredLocked(newTouchedWindowHandle)) {targetFlags | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;}BitSet32 pointerIds;if (isSplit) {pointerIds.markBit(entry.pointerProperties[0].id);}tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);}}}......// 检查是否允许注入所有已触摸的前台窗口并确保至少有一个已触摸的前台窗口{ // 进行必要的权限检查bool haveForegroundWindow false;for (const TouchedWindow touchedWindow : tempTouchState.windows) {if (touchedWindow.targetFlags InputTarget::FLAG_FOREGROUND) {haveForegroundWindow true;if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {injectionResult InputEventInjectionResult::PERMISSION_DENIED;injectionPermission INJECTION_PERMISSION_DENIED;goto Failed;}}}bool hasGestureMonitor !tempTouchState.gestureMonitors.empty();if (!haveForegroundWindow !hasGestureMonitor) {......injectionResult InputEventInjectionResult::FAILED;goto Failed;}// 权限允许注入所有已触摸的前台窗口injectionPermission INJECTION_PERMISSION_GRANTED;}// 检查监听外部触摸的窗口是否属于同一 UID如果设置了策略标志将不会向该窗口显示坐标信息......// Success! Output targets.injectionResult InputEventInjectionResult::SUCCEEDED;// 通过 addWindowTargetLocked 将 tempTouchState 的结果传给 inputTargetsfor (const TouchedWindow touchedWindow : tempTouchState.windows) {addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,touchedWindow.pointerIds, inputTargets);}for (const TouchedMonitor touchedMonitor : tempTouchState.gestureMonitors) {addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,touchedMonitor.yOffset, inputTargets);}// 丢弃外部或悬停触摸窗口因为我们在下一次迭代中不会关心它们// 裁剪 tempTouchStatetempTouchState.filterNonAsIsTouchWindows();Failed:// Check injection permission once and for all. -- 检查 injection 权限if (injectionPermission INJECTION_PERMISSION_UNKNOWN) {if (checkInjectionPermission(nullptr, entry.injectionState)) {injectionPermission INJECTION_PERMISSION_GRANTED;} else {injectionPermission INJECTION_PERMISSION_DENIED;}}if (injectionPermission ! INJECTION_PERMISSION_GRANTED) {return injectionResult;}// 如果 injector 获取到权限则更新最终的触摸状态if (!wrongDevice) {......// 保存 tempTouchState 到 mTouchStatesByDisplayif (maskedAction ! AMOTION_EVENT_ACTION_SCROLL) {if (tempTouchState.displayId 0) {mTouchStatesByDisplay[displayId] tempTouchState;} else {mTouchStatesByDisplay.erase(displayId);}}// Update hover state.-- 更新悬停状态mLastHoverWindowHandle newHoverWindowHandle;}return injectionResult; }InputDispatcher::findTouchedWindowTargetsLocked() 函数的核心流程如下 创建 TouchState 类型的 tempTouchState并将当前触摸状态赋值到 tempTouchState用于后续更新 mTouchStatesByDisplay调用 InputDispatcher::findTouchedWindowAtLocked() 函数寻找可接收当前触摸事件的窗口检验刚查找到的窗口的有效性并为窗口添加 Flag 标志位然后通过 TouchState::addOrUpdateWindow() 函数将其添加到 tempTouchState.windows 中保存经过重重的检验与判断操作如果没有问题则通过 addWindowTargetLocked() 函数将 tempTouchState 的结果传给 inputTargets函数的最后将 tempTouchState 保存到 mTouchStatesByDisplay 中以便下一次使用。 2.1 TouchState 结构体 TouchState 用来跟踪记录触摸事件状态其有一个窗口队列 std::vectorTouchedWindow windows 用来保存当前 Display 中所有可以接收 Motion 事件的窗口。 xref: /frameworks/native/services/inputflinger/dispatcher/TouchState.h struct TouchState {bool down;bool split;int32_t deviceId; // id of the device that is currently down, others are rejecteduint32_t source; // source of the device that is current down, others are rejectedint32_t displayId; // id to the display that currently has a touch, others are rejectedstd::vectorTouchedWindow windows;std::vectorspandroid::InputWindowHandle portalWindows;std::vectorTouchedMonitor gestureMonitors;TouchState();~TouchState();void reset();void copyFrom(const TouchState other);void addOrUpdateWindow(const spandroid::InputWindowHandle windowHandle, int32_t targetFlags, BitSet32 pointerIds);void addPortalWindow(const spandroid::InputWindowHandle windowHandle);void addGestureMonitors(const std::vectorTouchedMonitor monitors);void removeWindowByToken(const spIBinder token);void filterNonAsIsTouchWindows();void filterNonMonitors();spInputWindowHandle getFirstForegroundWindowHandle() const;bool isSlippery() const; };xref: /frameworks/native/services/inputflinger/dispatcher/TouchState.cpp TouchState::TouchState(): down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {}TouchState::~TouchState() {}void TouchState::reset() {down false;split false;deviceId -1;source 0;displayId ADISPLAY_ID_NONE;windows.clear();portalWindows.clear();gestureMonitors.clear(); }void TouchState::copyFrom(const TouchState other) {down other.down;split other.split;deviceId other.deviceId;source other.source;displayId other.displayId;windows other.windows;portalWindows other.portalWindows;gestureMonitors other.gestureMonitors; }void TouchState::filterNonAsIsTouchWindows() {for (size_t i 0; i windows.size();) {TouchedWindow window windows[i];if (window.targetFlags (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {window.targetFlags ~InputTarget::FLAG_DISPATCH_MASK;window.targetFlags | InputTarget::FLAG_DISPATCH_AS_IS;i 1;} else {windows.erase(windows.begin() i);}} } ......在 InputDispatcher::findTouchedWindowTargetsLocked() 函数的开始创建了一个 TouchState 类型的 tempTouchState每次开始寻找接收 Motion 的窗口前都先通过 mTouchStatesByDisplay.find(displayId) 根据传入的 displayId 获取到 oldState 并拷贝给 tempTouchState。那创建 tempTouchState 的作用是什么 在 InputDispatcher::findTouchedWindowTargetsLocked() 函数的最后会把函数开始时创建的 tempTouchState 保存到 mTouchStatesByDisplay 中因此在函数的开始 tempTouchState 获取到的是上一次寻找的结果。这么做的目的是为了下一次再进到这个函数时直接取上一次执行后的结果就不需要再次遍历所有的窗口来寻找可以接收当前输入事件的窗口比如在 DOWN 的时候遍历所有窗口找到了所有可以接收当前输入事件的窗口那么后续分发 MOVE、UP 等事件的时候不必重复再去遍历所有窗口进行查找提升效率。 2.2 InputDispatcher::findTouchedWindowAtLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp spInputWindowHandle InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,int32_t y, TouchState* touchState,bool addOutsideTargets,bool addPortalWindows,bool ignoreDragWindow) {if ((addPortalWindows || addOutsideTargets) touchState nullptr) {LOG_ALWAYS_FATAL(Must provide a valid touch state if adding portal windows or outside targets);}// Traverse windows from front to back to find touched window.const std::vectorspInputWindowHandle windowHandles getWindowHandlesLocked(displayId);for (const spInputWindowHandle windowHandle : windowHandles) {if (ignoreDragWindow haveSameToken(windowHandle, mDragState-dragWindow)) {continue;}const InputWindowInfo* windowInfo windowHandle-getInfo();if (windowInfo-displayId displayId) {auto flags windowInfo-flags;if (windowInfo-visible) { // 当前窗口必须是可见的// 窗口不能包含InputWindowInfo::Flag::NOT_TOUCHABLE 标志位设置了这个 flag 的窗口不能接收Motion事件if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {bool isTouchModal !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL);if (isTouchModal || windowInfo-touchableRegionContainsPoint(x, y)) {int32_t portalToDisplayId windowInfo-portalToDisplayId;if (portalToDisplayId ! ADISPLAY_ID_NONE portalToDisplayId ! displayId) {if (addPortalWindows) {// For the monitoring channels of the display.touchState-addPortalWindow(windowHandle);}return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,addOutsideTargets, addPortalWindows);}// Found window.return windowHandle;}}if (addOutsideTargets flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {touchState-addOrUpdateWindow(windowHandle,InputTarget::FLAG_DISPATCH_AS_OUTSIDE,BitSet32(0));}}}}return nullptr; }InputDispatcher::findTouchedWindowAtLocked() 函数通过遍历所有窗口寻找可以接收输入事件的窗口目标窗口需要满足以下条件 当前窗口必须是 visible 可见的当前窗口不能包含 InputWindowInfo::Flag::NOT_TOUCHABLE 标志位设置了这个 flag 的窗口不能接收 Motion 事件当前窗口不能包含 InputWindowInfo::Flag::NOT_FOCUSABLE 和 InputWindowInfo::Flag::NOT_TOUCH_MODAL 这两个 flag 标志位如果某个窗口没有 NOT_TOUCH_MODAL 这个 flag表示这个窗口将会消费掉所有坐标事件无论这些事件是否落在了这个窗口区域里面当前输入事件的坐标落在当前窗口的 Motion 区域里那么返回当前窗口。 2.3 TouchState::addOrUpdateWindow() xref: /frameworks/native/services/inputflinger/dispatcher/TouchState.cpp void TouchState::addOrUpdateWindow(const spInputWindowHandle windowHandle, int32_t targetFlags,BitSet32 pointerIds) {if (targetFlags InputTarget::FLAG_SPLIT) {split true;}for (size_t i 0; i windows.size(); i) {TouchedWindow touchedWindow windows[i];if (touchedWindow.windowHandle windowHandle) { // 如果已存在则直接更新对应的值touchedWindow.targetFlags | targetFlags;if (targetFlags InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {touchedWindow.targetFlags ~InputTarget::FLAG_DISPATCH_AS_IS;}touchedWindow.pointerIds.value | pointerIds.value;return;}}// 新建 TouchedWindow 并赋值然后加入 windows 中TouchedWindow touchedWindow; touchedWindow.windowHandle windowHandle;touchedWindow.targetFlags targetFlags;touchedWindow.pointerIds pointerIds;windows.push_back(touchedWindow); }TouchState::addOrUpdateWindow() 函数首先遍历 TouchState.windows 查找是否已存在同一个 TouchedWindow 实例对象如已存在则使用传入的参数更新其对应的值然后返回即可如不存在则新建一个 TouchWindow 对象整合之前的所有信息然后加入到 tempTouchState 的 windows 队列中。 2.4 InputDispatcher::addWindowTargetLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp void InputDispatcher::addWindowTargetLocked(const spInputWindowHandle windowHandle,int32_t targetFlags, BitSet32 pointerIds,std::vectorInputTarget inputTargets) {std::vectorInputTarget::iterator it std::find_if(inputTargets.begin(), inputTargets.end(),[windowHandle](const InputTarget inputTarget) {return inputTarget.inputChannel-getConnectionToken() windowHandle-getToken();});const InputWindowInfo* windowInfo windowHandle-getInfo();if (it inputTargets.end()) {InputTarget inputTarget;std::shared_ptrInputChannel inputChannel getInputChannelLocked(windowHandle-getToken());if (inputChannel nullptr) {ALOGW(Window %s already unregistered input channel, windowHandle-getName().c_str());return;}inputTarget.inputChannel inputChannel;inputTarget.flags targetFlags;inputTarget.globalScaleFactor windowInfo-globalScaleFactor;inputTarget.displaySize int2(windowHandle-getInfo()-displayWidth, windowHandle-getInfo()-displayHeight);inputTargets.push_back(inputTarget);it inputTargets.end() - 1;}ALOG_ASSERT(it-flags targetFlags);ALOG_ASSERT(it-globalScaleFactor windowInfo-globalScaleFactor);it-addPointers(pointerIds, windowInfo-transform); }std::find_ifC 中 STL 库中的一个函数它可以在一个给定的范围内查找第一个符合指定条件的元素。它接收一个范围和一个谓词即一个判断条件的函数作为参数返回第一个满足该条件的元素的迭代器。如果在整个范围内都找不到满足条件的元素则返回 last 参数指向的位置。 InputDispatcher::addWindowTargetLocked 函数执行流程如下 通过 std::find_if 库函数在 inputTargets 队列中检查当前焦点窗口是否已经在里面避免重复加入如果 inputTargets 里面还没有则根据焦点窗口的 IBinder 类型的 token 找到对应的 InputChannel然后根据该 InputWindowHandle 创建一个对应的 InputTarget 对象并添加到 inputTargets 中。 // All registered connections mapped by input channel token. std::unordered_mapspIBinder, spConnection, StrongPointerHashIBinder mConnectionsByTokenGUARDED_BY(mLock);在为 Server 端和 Client 端创建 InputChannel 对的时候会创建一个 BBinder 对象并将键值对 IBinder token, Connection connection 加入到了 InputDispatcher 维护的 mConnectionsByToken 中并且该 token 后续会返回给 InputWindowHandle那么就可以根据 InputWindowHandle 存储的 IBinder 对象找到一个对应的 Connection 对象进而找到一个 InputChannel 对象。 3.InputDispacher::dispatchEventLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,std::shared_ptrEventEntry eventEntry,const std::vectorInputTarget inputTargets) {......updateInteractionTokensLocked(*eventEntry, inputTargets);ALOG_ASSERT(eventEntry-dispatchInProgress); // should already have been set to true// 该方法会最终调用到 PowerManagerService 内部的 userActivityFromNative() 方法用来表示// 用户点击了当前界面主要用于屏保相关的倒计时打断处理pokeUserActivityLocked(*eventEntry);for (const InputTarget inputTarget : inputTargets) {// 通过 InputDispatcher::getConnectionLocked() 函数获取服务端 InputChannel 的 Connection 对象spConnection connection getConnectionLocked(inputTarget.inputChannel-getConnectionToken());if (connection ! nullptr) {prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);} else {......}} }InputDispatcher::dispatchEventLocked() 函数的核心作用如下 遍历传入的 inputTargets首先通过 InputDispatcher::getConnectionLocked() 函数获取服务端 InputChannel 的 Connection 对象调用 InputDispatcher.prepareDispatchCycleLocked() 函数来分发事件这里说明了对于一个输入事件来说会传递给多个窗口进行处理。对于每一个服务端 InputChannelInputDispatcher 都创建了一个 Connection 对象来保存这个 InputChannel 对象可以通过 IBinder 类型的 token 来检索得到该 Connection 对象那么这里便可以通过 InputTarget 的 InputChannel 对象中的 token 来得到持有服务端 InputChannel 的 Connection 对象。 4.InputDispacher::prepareDispatchCycleLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const spConnection connection,std::shared_ptrEventEntry eventEntry,const InputTarget inputTarget) {......// 如果连接状态不正常比如进程已经死了那么就不向它分发了跳过此事件。如果连接断开我们不希望将其他 outbound 事件排入队列if (connection-status ! Connection::STATUS_NORMAL) {#if DEBUG_DISPATCH_CYCLEALOGD(channel %s ~ Dropping event because the channel status is %s,connection-getInputChannelName().c_str(), connection-getStatusLabel());#endifreturn;}// Split a motion event if needed.-- FLAG_SPLIT用来声明当前输入事件是否支持被拆分分给多个窗口if (inputTarget.flags InputTarget::FLAG_SPLIT) {LOG_ALWAYS_FATAL_IF(eventEntry-type ! EventEntry::Type::MOTION,Entry type %s should not have FLAG_SPLIT,NamedEnum::string(eventEntry-type).c_str());const MotionEntry originalMotionEntry static_castconst MotionEntry(*eventEntry);if (inputTarget.pointerIds.count() ! originalMotionEntry.pointerCount) {std::unique_ptrMotionEntry splitMotionEntry splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);if (!splitMotionEntry) {return; // split event was dropped}if (DEBUG_FOCUS) {ALOGD(channel %s ~ Split motion event.,connection-getInputChannelName().c_str());logOutboundMotionDetails( , *splitMotionEntry);}enqueueDispatchEntriesLocked(currentTime, connection, std::move(splitMotionEntry),inputTarget);return;}}// 没有拆分则按原来的事件队列来分派事件enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); }InputDispatcher::prepareDispatchCycleLocked() 函数主要判断当前输入事件是否支持拆分如果支持拆分则把当前事件拆分。然后调用 InputDispatcher.enqueueDispatchEntriesLocked() 继续进行分发。 5.InputDispacher::enqueueDispatchEntriesLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const spConnection connection,std::shared_ptrEventEntry eventEntry,const InputTarget inputTarget) {if (ATRACE_ENABLED()) {std::string message StringPrintf(enqueueDispatchEntriesLocked(inputChannel%s, id0x% PRIx32 ),connection-getInputChannelName().c_str(), eventEntry-id);ATRACE_NAME(message.c_str());}bool wasEmpty connection-outboundQueue.empty();// Enqueue dispatch entries for the requested modes.enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_OUTSIDE);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_IS);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);// If the outbound queue was previously empty, start the dispatch cycle going.if (wasEmpty !connection-outboundQueue.empty()) {startDispatchCycleLocked(currentTime, connection);} }InputDispatcher::enqueueDispatchEntriesLocked() 函数的执行流程如下 调用 InputDispatcher::enqueueDispatchEntryLocked() 函数其作用是如果目标窗口设置了以下几种 flag表示目标窗口需要处理这类事件那么就把输入事件封装为相应的 DispatchEntry 类型加入到 Connection.outBoundQueue 队列中如果Coonection.outBoundQueue 队列之前为空但是经过 InputDispatcher::enqueueDispatchEntryLocked() 后不为空那么调用InputDispatcher::startDispatchCycleLocked() 函数开始分发事件。 6.InputDispacher::startDispatchCycleLocked() xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const spConnection connection) {......while (connection-status Connection::STATUS_NORMAL !connection-outboundQueue.empty()) {// Connection 的状态正常并且 Connection.outboundQueue 不为空DispatchEntry* dispatchEntry connection-outboundQueue.front(); // 获取 DispatchEntry 实例dispatchEntry-deliveryTime currentTime; // 设置分发事件的时间const std::chrono::nanoseconds timeout getDispatchingTimeoutLocked(connection-inputChannel-getConnectionToken());dispatchEntry-timeoutTime currentTime timeout.count(); // 设置分发事件的超时时间如果超时会引发 ANR// Publish the event. --- 发布事件status_t status;const EventEntry eventEntry *(dispatchEntry-eventEntry); // 获取待调度的事件switch (eventEntry.type) { // 根据 EventEntry::Type 类型分别调用不同的发布方法......case EventEntry::Type::MOTION: { // 分发 Motion 事件const MotionEntry motionEntry static_castconst MotionEntry(eventEntry);PointerCoords scaledCoords[MAX_POINTERS];const PointerCoords* usingCoords motionEntry.pointerCoords;......std::arrayuint8_t, 32 hmac getSignature(motionEntry, *dispatchEntry);// Publish the motion event.-- 发布 Motion 事件status connection-inputPublisher.publishMotionEvent(dispatchEntry-seq,dispatchEntry-resolvedEventId,motionEntry.deviceId, motionEntry.source,motionEntry.displayId, std::move(hmac),dispatchEntry-resolvedAction,motionEntry.actionButton,dispatchEntry-resolvedFlags,motionEntry.edgeFlags, motionEntry.metaState,motionEntry.buttonState,motionEntry.classification,dispatchEntry-transform,motionEntry.xPrecision, motionEntry.yPrecision,motionEntry.xCursorPosition,motionEntry.yCursorPosition,dispatchEntry-displaySize.x,dispatchEntry-displaySize.y,motionEntry.downTime, motionEntry.eventTime,motionEntry.pointerCount,motionEntry.pointerProperties, usingCoords);break;}case EventEntry::Type::FOCUS: {......}case EventEntry::Type::POINTER_CAPTURE_CHANGED: {......}case EventEntry::Type::DRAG: {......}case EventEntry::Type::CONFIGURATION_CHANGED:case EventEntry::Type::DEVICE_RESET:case EventEntry::Type::SENSOR: {......return;}}// Check the result.if (status) { // 检测分发的结果正常时status 0if (status WOULD_BLOCK) {if (connection-waitQueue.empty()) {......// 当前 waitQueue 是空的说明 socket 中也应该是空的但是却是 WOULD_BLOCK说明这时一个异常的情况中断分发abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);} else {...... // socket满了等待应用进程处理掉一些事件}} else {......abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);}return;}// Re-enqueue the event on the wait queue.// 将已经分发的事件 dispatchEntry 从 outboundQueue 中移除connection-outboundQueue.erase(std::remove(connection-outboundQueue.begin(),connection-outboundQueue.end(),dispatchEntry));traceOutboundQueueLength(*connection);// 将已经分发的事件 dispatchEntry 加入目标窗口 waitQueue中记录下已经分发到目标窗口侧的事件便于监控 ANR 等行为connection-waitQueue.push_back(dispatchEntry);// 如果目标窗口进程例如应用进程可响应则将这个事件超时事件点和目标窗口连接对象 token 加入 mAnrTracker 中监控// 如果不可响应则不再向它分发更多的事件直到它消耗了已经分发给它的事件if (connection-responsive) {mAnrTracker.insert(dispatchEntry-timeoutTime,connection-inputChannel-getConnectionToken());}traceWaitQueueLength(*connection); //systrace 中跟踪 waitQueue 的长度} }InputDispatcher::startDispatchCycleLocked() 函数中如果 Connection 的状态正常并且 Connection.outboundQueue 不为空则循环遍历 Connection.outboundQueue然后根据 EventEntry::Type 类型分别调用不同的发布方法这里我们只关心 Motion 类型的事件发布。 7.InputPublisher::publishMotionEvent() xref: /frameworks/native/libs/input/InputTransport.cpp status_t InputPublisher::publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,std::arrayuint8_t, 32 hmac, int32_t action, int32_t actionButton, int32_t flags,int32_t edgeFlags, int32_t metaState, int32_t buttonState,MotionClassification classification, const ui::Transform transform, float xPrecision,float yPrecision, float xCursorPosition, float yCursorPosition, int32_t displayWidth,int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {......InputMessage msg;msg.header.type InputMessage::Type::MOTION;msg.header.seq seq;msg.body.motion.eventId eventId;msg.body.motion.deviceId deviceId;msg.body.motion.source source;msg.body.motion.displayId displayId;msg.body.motion.hmac std::move(hmac);msg.body.motion.action action;msg.body.motion.actionButton actionButton;msg.body.motion.flags flags;msg.body.motion.edgeFlags edgeFlags;msg.body.motion.metaState metaState;msg.body.motion.buttonState buttonState;msg.body.motion.classification classification;msg.body.motion.dsdx transform.dsdx();msg.body.motion.dtdx transform.dtdx();msg.body.motion.dtdy transform.dtdy();msg.body.motion.dsdy transform.dsdy();msg.body.motion.tx transform.tx();msg.body.motion.ty transform.ty();msg.body.motion.xPrecision xPrecision;msg.body.motion.yPrecision yPrecision;msg.body.motion.xCursorPosition xCursorPosition;msg.body.motion.yCursorPosition yCursorPosition;msg.body.motion.displayWidth displayWidth;msg.body.motion.displayHeight displayHeight;msg.body.motion.downTime downTime;msg.body.motion.eventTime eventTime;msg.body.motion.pointerCount pointerCount;for (uint32_t i 0; i pointerCount; i) {msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);}return mChannel-sendMessage(msg); }InputPublisher::publishMotionEvent() 函数把输入事件的信息重新封装到一个 InputMessage 类型的结构体中然后调用 InputChannel::sendMessage() 函数发送封装好的 InputMessage。 8.InputChannel::sendMessage() xref: /frameworks/native/libs/input/InputTransport.cpp status_t InputChannel::sendMessage(const InputMessage* msg) {const size_t msgLength msg-size();InputMessage cleanMsg;msg-getSanitizedCopy(cleanMsg);ssize_t nWrite;do {nWrite ::send(getFd(), cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);} while (nWrite -1 errno EINTR);......return OK; }InputChannel::sendMessage() 函数调用 socket 的 send() 函数向服务端 InputChannel 保存的 socket 文件描述符发送封装好的 InputMessage 信息。 在 ViewRootImpl#setView() 函数中会创建一个 WindowInputEventReceiver 对象进而会构建一个 Native 层的 NativeInputEventReceiver 对象并且在初始化的过程中会调用 NativeInputEventReceiver::setFdEvents() 函数 xref: /frameworks/base/core/jni/android_view_InputEventReceiver.cpp status_t NativeInputEventReceiver::initialize() {setFdEvents(ALOOPER_EVENT_INPUT);return OK; } ...... void NativeInputEventReceiver::setFdEvents(int events) {if (mFdEvents ! events) {mFdEvents events;int fd mInputConsumer.getChannel()-getFd();if (events) {mMessageQueue-getLooper()-addFd(fd, 0, events, this, nullptr);} else {mMessageQueue-getLooper()-removeFd(fd);}} }NativeInputEventReceiver::setFdEvents() 函数通过 MessageQueue 的 Looper 对象的 addFd() 函数来对客户端 InputChannel 中保存的客户端 socket 文件描述符进行监听如果服务端有数据写入那么就调用 NativeInputEventReceiver::handleEvent() 函数回调。 9.NativeInputEventReceiver::handleEvent() xref: /frameworks/base/core/jni/android_view_InputEventReceiver.cpp int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {......if (events ALOOPER_EVENT_INPUT) { // ALOOPER_EVENT_INPUT 表示文件描述符可读JNIEnv* env AndroidRuntime::getJNIEnv();status_t status consumeEvents(env, false /*consumeBatches*/, -1, nullptr);mMessageQueue-raiseAndClearException(env, handleReceiveCallback);return status OK || status NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;}......return KEEP_CALLBACK; }在 NativeInputEventReceiver::initialize() 函数注册文件描述监听时传入的 events 类型就是 ALOOPER_EVENT_INPUT因此流程走 ALOOPER_EVENT_INPUT 流程继续调用 NativeInputEventReceiver::consumeEvents() 函数。 10.NativeInputEventReceiver::consumeEvents() xref: /frameworks/base/core/jni/android_view_InputEventReceiver.cpp status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {......if (consumeBatches) {mBatchedInputEventPending false;}if (outConsumedBatch) {*outConsumedBatch false;}ScopedLocalRefjobject receiverObj(env, nullptr);bool skipCallbacks false;for (;;) {uint32_t seq;InputEvent* inputEvent;// 读取客户端 InputChannel 发送的事件并转化为 InputEventstatus_t status mInputConsumer.consume(mInputEventFactory,consumeBatches, frameTime, seq, inputEvent);......if (!skipCallbacks) {// 获取到 Java 对象 InputEventReceiver 对象if (!receiverObj.get()) {receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));......}jobject inputEventObj;switch (inputEvent-getType()) {......case AINPUT_EVENT_TYPE_MOTION: {if (kDebugDispatchCycle) {ALOGD(channel %s ~ Received motion event., getInputChannelName().c_str());}// 将 Native 层输入事件对象转换为上层输入事件对象MotionEvent* motionEvent static_castMotionEvent*(inputEvent);if ((motionEvent-getAction() AMOTION_EVENT_ACTION_MOVE) outConsumedBatch) {*outConsumedBatch true;}inputEventObj android_view_MotionEvent_obtainAsCopy(env, motionEvent);break;}......}if (inputEventObj) {if (kDebugDispatchCycle) {ALOGD(channel %s ~ Dispatching input event., getInputChannelName().c_str());}// 最后通过 JNI 调用 Java 层对象 InputEventReceiver 的 dispatchInputEvent 方法env-CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);if (env-ExceptionCheck()) {ALOGE(Exception dispatching input event.);skipCallbacks true;}env-DeleteLocalRef(inputEventObj);}......}if (skipCallbacks) {mInputConsumer.sendFinishedSignal(seq, false);}} }NativeInputEventReceiver::consumeEvents() 函数的核心流程如下 调用 InputConsumer::comsume() 函数其通过客户端对应的 InputChannel::receiveMessage() 函数读取发送过来的输入事件并对不同的事件类型创建对应的事件对象如InputMessage::Type::MOTION 类型的事件为其创建 MotionEvent 对象并将刚读取的 InputMessage 的信息取出传给新建的 MotionEvent 对象也即将输入事件最终转化为一个 InputEvent 对象MotionEvent 是 InputEvent 的子类获取 Java 层 InputEventReceiver 对象在 ViewRootImpl 中创建 WindowInputEventReceiver 的时候会把 Java 层的 InputEventReceiver 对象的弱引用作为初始化 NativeInputEventReceiver 的参数后续 NativeInputEventReceiver 的全局变量 mReceiverWeakGlobal 便会持有这个引用通过 android_view_MotionEvent_obtainAsCopy() 函数将 Native 层输入事件对象转换为 Java 层的输入事件对象综述分析最后通过 JNI 调用到 InputEventReceiver#dispatchInputEvent() 函数将输入事件传递给 Java 层。 11.InputEventReceiver#dispatchInputEvent() xref: /frameworks/base/core/java/android/view/InputEventReceiver.java public abstract class InputEventReceiver {private static final String TAG InputEventReceiver;......// Called from native code.SuppressWarnings(unused)UnsupportedAppUsage(maxTargetSdk Build.VERSION_CODES.R, trackingBug 170729553)private void dispatchInputEvent(int seq, InputEvent event) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event);}...... }InputEventReceiver 是一个抽象类由于我们分析的是 ViewRootImpl#setView() 函数中的监听输入事件注册流程因此这里 InputEventReceiver 的实现是 ViewRootImpl 的内部类 WindowInputEventReceiver 类。 12.ViewRootImpl#WindowInputEventReceiver#onInputEvent() xref: /frameworks/base/core/java/android/view/ViewRootImpl.java#WindowInputEventReceiver.java final class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);}Overridepublic void onInputEvent(InputEvent event) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, processInputEventForCompatibility);ListInputEvent processedEvents;try {processedEvents mInputCompatProcessor.processInputEventForCompatibility(event);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}if (processedEvents ! null) {if (processedEvents.isEmpty()) {// InputEvent consumed by mInputCompatProcessorfinishInputEvent(event, true);} else {for (int i 0; i processedEvents.size(); i) {enqueueInputEvent(processedEvents.get(i), this,QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);}}} else {enqueueInputEvent(event, this, 0, true);}} }ViewRootImpl#WindowInputEventReceiver#onInputEvent() 函数继续调用 ViewRootImpl#enqueueInputEvent() 函数进行处理。 13.ViewRootImpl#enqueueInputEvent() xref: /frameworks/base/core/java/android/view/ViewRootImpl.java SuppressWarnings({EmptyCatchBlock, PointlessBooleanExpression}) public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {......UnsupportedAppUsagevoid enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) {QueuedInputEvent q obtainQueuedInputEvent(event, receiver, flags);if (event instanceof MotionEvent) {MotionEvent me (MotionEvent) event;if (me.getAction() MotionEvent.ACTION_CANCEL) { // 事件取消EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, Motion - Cancel,getTitle());}} else if (event instanceof KeyEvent) {KeyEvent ke (KeyEvent) event;if (ke.isCanceled()) {EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, Key - Cancel,getTitle());}}// Always enqueue the input event in order, regardless of its time stamp.// We do this because the application or the IME may inject key events// in response to touch events and we want to ensure that the injected keys// are processed in the order they were received and we cannot trust that// the time stamp of injected events are monotonic.QueuedInputEvent last mPendingInputEventTail;if (last null) {mPendingInputEventHead q;mPendingInputEventTail q;} else {last.mNext q;mPendingInputEventTail q;}mPendingInputEventCount 1;Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,mPendingInputEventCount);if (processImmediately) {doProcessInputEvents();} else {scheduleProcessInputEvents();}}...... }ViewRootImpl#enqueueInputEvent() 函数首先调用 ViewRootImpl#obtainQueuedInputEvent() 函数获取一个 QueuedInputEvent 实例对象并将其添加到 mPendingInputEventTail 输入事件的队列尾部由于传递进来的值 processImmediately 为 true因此调用 ViewRootImpl#doProcessInputEvents() 函数来处理输入事件。 14.ViewRootImpl#doProcessInputEvents() xref: /frameworks/base/core/java/android/view/ViewRootImpl.java SuppressWarnings({EmptyCatchBlock, PointlessBooleanExpression}) public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {......void doProcessInputEvents() {// 交付队列中所有挂起的输入事件while (mPendingInputEventHead ! null) {QueuedInputEvent q mPendingInputEventHead;mPendingInputEventHead q.mNext;if (mPendingInputEventHead null) {mPendingInputEventTail null;}q.mNext null;mPendingInputEventCount - 1;Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,mPendingInputEventCount);mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));deliverInputEvent(q);}// We are done processing all input events that we can process right now// so we can clear the pending flag immediately.if (mProcessInputEventsScheduled) {mProcessInputEventsScheduled false;mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);}}...... }ViewRootImpl#doProcessInputEvents() 函数遍历取出 mPendingInputEventHead 中的输入事件转交给 ViewRootImpl#deliverInputEvent() 函数来处理刚取出的 QueuedInputEvent。 15.ViewRootImpl#deliverInputEvent() xref: /frameworks/base/core/java/android/view/ViewRootImpl.java SuppressWarnings({EmptyCatchBlock, PointlessBooleanExpression}) public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {......private void deliverInputEvent(QueuedInputEvent q) {Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, deliverInputEvent,q.mEvent.getId());......try {if (mInputEventConsistencyVerifier ! null) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, verifyEventConsistency);try {mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}InputStage stage;if (q.shouldSendToSynthesizer()) {stage mSyntheticInputStage;} else {stage q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;}if (q.mEvent instanceof KeyEvent) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, preDispatchToUnhandledKeyManager);try {mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}if (stage ! null) {handleWindowFocusChanged();// 分发要处理的事件stage.deliver(q);} else {finishInputEvent(q);}} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}...... }ViewRootImpl#deliverInputEvent() 函数在处理输入事件之前如果 InputEventConsistencyVerifier 对象不为空则调用 InputEventConsistencyVerifier#onInputEvent() 函数来验证输入事件的一致性。随后调用 InputStage#deliver() 函数进行事件分发InputStage 代表了输入事件的处理阶段使用责任链设计模式限于篇幅问题后续有时间再继续 ViewRootImpl 事件派发流程。 小结 通过跟踪代码进行深入分析在此对 InputDispatcher 分发 Motion 事件做个小结 首先根据事件的类型寻找所有可以接收当前输入事件的窗口并构建一个 InputTarget 队列存放 InputDispacher::findTouchedWindowTargetsLocked() 函数获取到的目标窗口遍历 InputTarget 队列通过 InputDispatcher::getConnectionLocked() 函数获取服务端 InputChannel 的 Connection 对象由 Connection 对象的 InputPublishe 调用其 publishMotionEvent() 函数将输入事件信息封装成 InputMessage 对象然后通过服务端 InputChannel 调用 socket 的 send 函数将 InputMessage 写入服务端 socket 的发送缓冲区客户端通过注册的 NativeInputEventReceiver 的 Looper 监听到客户端 socket 的接收缓冲区有数据写入则回调NativeInputEventReceiver::handleEvent() 函数收集服务端传过来的 InputMessage 信息最终将输入事件封装为 Java 层的 MotionEvent 等类型然后调用 Java 层的 InputEventReceiver#DispatchInputEvent() 函数完成输入事件从 Native 层到 Java 层的传递此时输入事件从 InputDispatcher 分发给对应的 ViewRootImpl由其继续进行事件分发。 总结 InputDispatcher 分发事件的流程分析完毕此时输入事件交给对应的 ViewRootImpl 由其继续进行事件分发后续有时间会继续深入学习 ViewRootImpl 的事件分发流程并产出文章。本文如有错误还望大家帮助纠正互相探讨学习哈
http://www.pierceye.com/news/322198/

相关文章:

  • 1688货源网官方网站网站怎么做背景
  • 做阿里还是网站中小企业为什么要建设网站
  • 天津的网站建设做网站费用怎么入账
  • 网站原型是产品经理做wordpress手机上用的
  • 专业网站排名优化重庆广告公司电话
  • 在线免费看影视网站正规企业展厅设计公司
  • 励志网站织梦源码做电子杂志用什么网站
  • 电子设计网站wordpress数据清除
  • 义乌网站推广中国住房和城乡建设厅网站
  • 濮阳seo网站建设商贸公司寮步网站建设
  • 百姓网网站建设如何在社交网站做销售
  • 网站微信认证费用介绍网络营销的短文
  • 北京微网站修改wordpress后台登陆
  • 网站建设管理工作情况报告企业在线
  • ps临摹网站营销型网站传统网站
  • 对电子商务网站建设和管理的理解学网站开发应该学什么软件
  • 建设网站的app英文成品网站模板下载
  • 破解版软件下载网站网站图片处理方案
  • 安徽网站建设方案服务汉中建设工程招标网
  • 网站建设公司企业模板下载阿里巴巴官网国际站
  • icp备案网站信息修改百度小说排行榜总榜
  • 崇明专业网站建设做网站后台要学什么
  • 专门做搜索种子的网站有哪些吉林平台网站建设多少钱
  • seo网站优化案例高端品牌裙子
  • 合肥需要做网站的公司无锡工程建设信息网站
  • 网站服务器有哪几种做招聘网站没有数据
  • 合肥手机网站制作建设自己做视频的网站
  • 公司网站备案名称广东建设项目备案公示网站
  • 网站建设设计维片长治网站建设公司
  • 商务网站建设兴田德润电话多少世界著名网站开发语言