本文是一篇很长~很长~很长~~~的技术笔记
如果有什么问题,欢迎指正.
前边儿已经说过,要想了解鼠标就要研究三个东西
不论是鼠标挂载/卸载还是鼠标操作,基本上算是对输入事件的一个监听了,属于一类东西
但是鼠标绘制,这涉及到surface绘制鼠标,对于我本人,基本上没接触过那么多绘制的东西,一点点尝试分析.
鼠标事件本身又包括什么?
接下来实现一个阶段性的小目标,就先愉快的分析一下鼠标事件的监听吧.
ok,怎么分析?
从宏观角度出发,千万别一头扎进某个小片段里.
先整体看下整个流程,再来特殊分析.
鼠标也属于输入设备对吧,那鼠标是不是遵循针对输入设备的处理逻辑?
你总不至于给每一种输入设备都配置一套独特的逻辑吧?那岂不是太冗余了.
逻辑是一套,只不过在处理时需要区分一下type而已.
所以呢,本文就来分析手机是如何监听输入事件InputEvent的?
这个有时候也需要一些基础和经验.
源码中管理输入的服务是什么?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.
那为什么要分两个线程呢?
其实很好理解,就像是饭店门口总有迎宾带路的,坐下之后又会有点餐员一个道理.两个角色都需要等待,也就是都会有阻塞.
迎宾的服务员相当于是在一直监听有哪些顾客进来,并把这些顾客带到对应位置,对于迎宾员而言,只知道顾客是来吃饭的,不知道具体吃什么.
点餐员相当于需要去记录对应顾客的菜单menu,并且分发给对应厨师来进行处理.
那么饭店相当于什么呢?饭店是不是相当于一个储存消息的队列?迎宾员把顾客放到这个队列,之后点餐员开始和队列中的顾客接洽,并生成顾客的菜单传递给厨师.
那为什么要分两个人处理呢?如果一个人会是什么情况?
比如来了一波儿顾客,服务员需要把他领导座位,并且下单结束,之后再把单交给厨师.如果在这个过程中又来了一波儿人,那就只能等待….
随便问一句,现在谁有那么多耐心去等啊.更别说电子设备这种需要及时响应的了.
从这儿看,生活真是无处不符合逻辑知识啊~
输入事件的处理也是这个逻辑.流程也就出来了
InputReaderThread需要在系统准备好就要开启监听输入事件的到来,相当于饭店营业开始时,迎宾人员开始等候
在InputReaderThread监听到输入事件时,就要把原始事件rawEvent插入到队列中,相当于一层透传,也就相当于迎宾人员将顾客带到座位
至此,InputReaderThread的任务就完成了,接下来就该点餐员上场了
InputDispatcherThread要干什么?要循环从队列中取数据,也就是要去观察是否有新的顾客落座,并开始下单.
下单的过程也是相当于一个处理的过程,完成了原始事件到系统特定输入事件的转换rawEvent--->inputEvent.
紧接着就是把菜单交给厨师,任务over
到这儿dispatcher的任务也完成了,接下来就是需要厨师根据菜单做菜了.这就相当于不同应用对应不同输入事件的处理.
比如在应用中监听按钮点击事件,当点击按钮时会触发该事件.
在这个过程中,有一个逻辑,点餐员在下完单之后需要告诉迎宾员吗?不需要的,所以呀,这个事件的处理是单向的.
分两个线程处理可以实现快速响应输入事件.
reader线程监听到事件后直接插入到队列中,就可以继续监听,来保证缩短用户输入开始到接收到输入事件的时间
dispatcher线程会一直取出新的输入事件,重点是异步分发给对应应用处理.缩短分发时间.
好了,为了方便理解和记忆,以上都是口语化的解释,接下来该官方描述一下了
理论结束,接下来大致看一下代码:
InputManager.h中超级简单,四个filed,五个方法(排除掉构造方法)
1public:
2 virtual status_t start();//开启两个线程(执行run方法)
3 virtual status_t stop();//退出两个线程(requestExitAndWait)
4
5 virtual sp getReader() ;
6 virtual sp getDispatcher() ;
7
8private:
9 sp mReader;
10 sp mReaderThread;//readerThread实例
11
12 sp mDispatcher;
13 sp mDispatcherThread;//dispatcherThread实例
14
15 void initialize();//用于初始化两个thread对象
16};
方法实现也很简单,不再贴出.接下来看下两个线程做了什么?
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的线程做了什么?
1bool InputReaderThread::threadLoop() {
2 mReader->loopOnce();
3 return true;
4}
就是这么easy,触发了InputReader的loopOnce.一级级的追吧