🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
在了解了INotify与Epoll的基础知识之后便可以正是开始分析Reader子系统的工作原理了。首先要理解InputReader的运行方式。在5.1.3节介绍了InputReader被InputManager创建,并运行于InputReaderThread线程中。InputReader如何在InputReaderThread中运行呢? InputReaderThread继承自C++的Thread类,Thread类封装了pthread线程工具,提供了与Java层Thread类相似的API。C++的Thread类提供了一个名为threadLoop()的纯虚函数,当线程开始运行后,将会在内建的线程循环中不断地调用threadLoop(),直到此函数返回false,则退出线程循环,从而结束线程。 InputReaderThread仅仅重写了threadLoop()函数: **InputReader.cpp-->InputReaderThread::threadLoop()** ``` bool InputReaderThread::threadLoop() { mReader->loopOnce(); // 执行InputReader的loopOnce()函数 returntrue; } ``` InputReaderThread启动后,其线程循环将不断地执行InputReader.loopOnce()函数。因此这个loopOnce()函数作为线程循环的循环体包含了InputReader的所有工作。 * * * * * **注意**: C++层的Thread类与Java层的Thread类有着一个显著的不同。C++层Thread类内建了线程循环,threadLoop()就是一次循环而已,只要返回值为true,threadLoop()将会不断地被内建的循环调用。这也是InputReader.loopOnce()函数名称的由来。而Java层Thread类的run()函数则是整个线程的全部,一旦其退出,线程也便完结。 * * * * * 接下来看一下InputReader.loopOnce()的代码,分析一下InputReader在一次线程循环中做了什么。 **InputReader.cpp-->InputReader::loopOnce()** ``` void InputReader::loopOnce() { ...... /* **① 通过EventHub抽取事件列表**。读取的结果存储在参数mEventBuffer中,返回值表示事件的个数 *当EventHub中无事件可以抽取时,此函数的调用将会阻塞直到事件到来或者超时 */ size_tcount = mEventHub->getEvents(timeoutMillis ,mEventBuffer, EVENT_BUFFER_SIZE); { AutoMutex _l(mLock); ...... if(count) { // **② 如果有抽得事件,则调用processEventsLocked()函数对事件进行加工处理** processEventsLocked(mEventBuffer, count); } ...... } ...... /* **③ 发布事件。** processEventsLocked()函数在对事件进行加工处理之后,便将处理后的事件存储在 mQueuedListener中。在循环的最后,通过调用flush()函数将所有事件交付给InputDispatcher */ mQueuedListener->flush(); } ``` InputReader的一次线程循环的工作思路非常清晰,一共三步: - 首先从EventHub中抽取未处理的事件列表。这些事件分为两类,一类是从设备节点中读取的原始输入事件,另一类则是输入设备可用性变化事件,简称为设备事件。 - 通过processEventsLocked()对事件进行处理。对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果暂存到mQueuedListener中。 - 所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher。 这便是InputReader的总体工作流程。而我们接下来将详细讨论这三步的实现。