Android源码分析之鼠标事件监听(二)

2019-07-10 16:07:16 浏览数 (2)

本文是一篇很长~很长~很长~~~的技术笔记 如果有什么问题,欢迎指正.

Zero 前言

前边儿已经说过,要想了解鼠标就要研究三个东西

  • 鼠标的挂载/卸载
  • 鼠标事件处理,也就是鼠标操作
  • 鼠标绘制

不论是鼠标挂载/卸载还是鼠标操作,基本上算是对输入事件的一个监听了,属于一类东西 但是鼠标绘制,这涉及到surface绘制鼠标,对于我本人,基本上没接触过那么多绘制的东西,一点点尝试分析. 鼠标事件本身又包括什么?

  • 事件监听
  • 事件处理

接下来实现一个阶段性的小目标,就先愉快的分析一下鼠标事件的监听吧. ok,怎么分析? 从宏观角度出发,千万别一头扎进某个小片段里. 先整体看下整个流程,再来特殊分析. 鼠标也属于输入设备对吧,那鼠标是不是遵循针对输入设备的处理逻辑? 你总不至于给每一种输入设备都配置一套独特的逻辑吧?那岂不是太冗余了. 逻辑是一套,只不过在处理时需要区分一下type而已. 所以呢,本文就来分析手机是如何监听输入事件InputEvent的?

One IMS

这个有时候也需要一些基础和经验. 源码中管理输入的服务是什么?IMS(InputManagerService) 那就从InputManager.h(目录位于/frameworks/native/services/inputflinger/)出发 为啥我先看头文件? 第一,头文件一般会有注释说明,交代整个文件作用,便于代码理解 第二,头文件可以直接看出代码结构structure.一眼就可以知道有哪些方法,哪些filed 果然没让我失望,这个文件里的注释完全表明了framework层所做的工作. 原文就不贴了,有想看的可以在线查看源码,传送门-->http://androidxref.com/9.0.0_r3/xref/frameworks/native/services/inputflinger/InputManager.h 意思是什么,IM是系统核心进程(这不是废话吗?要不然怎么输入). 接下来关键了 说IM管理着两个进程InputReaderThread和InputDispatcherThread,也终于找到了管理输入的重点 顾名思义,一个负责(read)读取inputEvent,一个负责(dispatch)分发inputEvent,各司其职 读取时的事件成为原始输入事件,叫做rawEvent.分发出去的是处理之后的输入事件inputEvent. 那为什么要分两个线程呢?

TWO 饭来了

其实很好理解,就像是饭店门口总有迎宾带路的,坐下之后又会有点餐员一个道理.两个角色都需要等待,也就是都会有阻塞. 迎宾的服务员相当于是在一直监听有哪些顾客进来,并把这些顾客带到对应位置,对于迎宾员而言,只知道顾客是来吃饭的,不知道具体吃什么. 点餐员相当于需要去记录对应顾客的菜单menu,并且分发给对应厨师来进行处理. 那么饭店相当于什么呢?饭店是不是相当于一个储存消息的队列?迎宾员把顾客放到这个队列,之后点餐员开始和队列中的顾客接洽,并生成顾客的菜单传递给厨师. 那为什么要分两个人处理呢?如果一个人会是什么情况? 比如来了一波儿顾客,服务员需要把他领导座位,并且下单结束,之后再把单交给厨师.如果在这个过程中又来了一波儿人,那就只能等待…. 随便问一句,现在谁有那么多耐心去等啊.更别说电子设备这种需要及时响应的了. 从这儿看,生活真是无处不符合逻辑知识啊~ 输入事件的处理也是这个逻辑.流程也就出来了 InputReaderThread需要在系统准备好就要开启监听输入事件的到来,相当于饭店营业开始时,迎宾人员开始等候 在InputReaderThread监听到输入事件时,就要把原始事件rawEvent插入到队列中,相当于一层透传,也就相当于迎宾人员将顾客带到座位 至此,InputReaderThread的任务就完成了,接下来就该点餐员上场了 InputDispatcherThread要干什么?要循环从队列中取数据,也就是要去观察是否有新的顾客落座,并开始下单. 下单的过程也是相当于一个处理的过程,完成了原始事件到系统特定输入事件的转换rawEvent--->inputEvent. 紧接着就是把菜单交给厨师,任务over 到这儿dispatcher的任务也完成了,接下来就是需要厨师根据菜单做菜了.这就相当于不同应用对应不同输入事件的处理. 比如在应用中监听按钮点击事件,当点击按钮时会触发该事件. 在这个过程中,有一个逻辑,点餐员在下完单之后需要告诉迎宾员吗?不需要的,所以呀,这个事件的处理是单向的. 分两个线程处理可以实现快速响应输入事件. reader线程监听到事件后直接插入到队列中,就可以继续监听,来保证缩短用户输入开始到接收到输入事件的时间 dispatcher线程会一直取出新的输入事件,重点是异步分发给对应应用处理.缩短分发时间. 好了,为了方便理解和记忆,以上都是口语化的解释,接下来该官方描述一下了

Three 源码

  • InputReaderThread:用于读取和预处理原始输入事件,并且把事件插入到由dispatcherThread管理的队列中.在输入事件来临前一直阻塞
  • InputDispatcherThread:等待队列中有新的输入事件(来临前阻塞),并且异步分发给对应的应用

理论结束,接下来大致看一下代码:

InputManager

InputManager.h中超级简单,四个filed,五个方法(排除掉构造方法)

代码语言:javascript复制
 1public:
 2    virtual status_t start();//开启两个线程(执行run方法)
 3   virtual status_t stop();//退出两个线程(requestExitAndWait)
 4
 5    virtual sp<InputReaderInterface> getReader();
 6    virtual sp<InputDispatcherInterface> getDispatcher();
 7
 8private:
 9    sp<InputReaderInterface> mReader;
10    sp<InputReaderThread> mReaderThread;//readerThread实例
11
12    sp<InputDispatcherInterface> mDispatcher;
13    sp<InputDispatcherThread> mDispatcherThread;//dispatcherThread实例
14
15    void initialize();//用于初始化两个thread对象
16};

方法实现也很简单,不再贴出.接下来看下两个线程做了什么?

InputReader

InputReaderThread在InputReader文件中.目录为: /frameworks/native/services/inputflinger/InputReader.cpp 传送门---> http://androidxref.com/9.0.0_r3/xref/frameworks/native/services/inputflinger/InputReader.cpp 在线程run之后,会触发threadLoop方法,来看看InputReaderThread的线程做了什么?

代码语言:javascript复制
1bool InputReaderThread::threadLoop() {
2    mReader->loopOnce();
3    return true;
4}

就是这么easy,触发了InputReader的loopOnce.一级级的追吧

0 人点赞