A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 Android_Robot 于 2016-10-19 15:16 编辑

Android事件分发完全解析(下)

上一节Android事件分发完全解析(上)中我们简略地分析了事件分发机制的由来,这里要说明一点,Android(或者说任何的驱动系统)都包含大量不同类型的事件,比如按键啦、轨迹球啦、鼠标啦、触摸啦、红外线啦等等等,这里为了简化问题也为了切合实际,我们只针对触摸事件进行分析,至于其他的一些杂七杂八的事件其实都很好理解就不多说了。


那么在Android中一个触摸事件究竟是从何而来的呢?


对事件分发稍有了解的童鞋一定知道dispatchtouchevent方法,都知道View对触摸事件进行分发的起点,但是传入dispatchtouchevent方法中的触摸事件又是从何而来的呢?


往上一步步追踪你会发现代码调用无穷无尽找不到头……有时候盲目地去read fuck source code反而会让你更困惑,其实用脑子想想理清逻辑就可以很快找到答案,我们都知道一个事件的产生肯定需要用户的交互,也就是说,只有当用户触摸屏幕或按下某个按键之类的操作之后系统才能做出事件响应,而每一个这样的操作我们都可将其当作事件的“源头”,那么捕获这些最原始交互信息的猎手应该是谁呢?还会是View?还会是Activity?还会是ViewRootImpl还会是WMS吗?


这些framework中的构件相对于更底层的机制来说还是太“高级”了,我们知道Android是基于Linux的一款操作系统,Linux其本身就有一个很Perfect的Input子系统架构,Android虽然也实现了几个属于自己的机制,但是大部分底层的调用还是基于Linux所提供的操作接口,比如对Input驱动的编写就是基于Linux Input系统字符驱动的操作接口,关于Linux的这部分大家如果有兴趣可以去看看私房菜,这里就不多扯了,这里你仅需要知道在Android中Linux的Input子系统会在/dev/input/路径下读写以event[NUMBER]为名的硬件输入设备节点。


这些节点都是跟具体硬件有关的,所以呢可能每一款设备的具体节点名都是不一样的,比如在我的mx3中/dev/input/event0为mxhub-key而/dev/input/event1为gp2ap。具体的节点信息可通过Android提供的getevent工具查看,如果你的设备已经连接了PC或者模拟器已启动,adb shell后getevent即可获取事件读写的实时状态,当然各个设备是不一样的,比如mx3中通过getevent查看所有Input节点:



  1. <font color="#444444"><font color="#000"><font size="3">adb shell  
  2. shell@mx3:/ $ getevent  
  3. getevent  
  4. add device 1: /dev/input/event0  
  5.   name:     "mxhub-keys"  
  6. add device 2: /dev/input/event4  
  7.   name:     "lsm330dlc_gyr"  
  8. add device 3: /dev/input/event3  
  9.   name:     "lsm330dlc_acc"  
  10. add device 4: /dev/input/event1  
  11.   name:     "gp2ap"  
  12. could not get driver version for /dev/input/mouse0, Not a typewriter  
  13. add device 5: /dev/input/event5  
  14.   name:     "mx_ts"  
  15. add device 6: /dev/input/event6  
  16.   name:     "gpio-keys"  
  17. add device 7: /dev/input/event7  
  18.   name:     "Headset"  
  19. add device 8: /dev/input/event2  
  20.   name:     "compass"  
  21. could not get driver version for /dev/input/mice, Not a typewriter </font></font></font>
复制代码

可见mx3中有8个Input子系统,分别为:

  • 位于event0节点下读写魅族呼吸灯按钮也就是屏幕下方圆形的那个发光主键的“mxhub-keys”子系统
  • 位于event4节点下读写重力传感器的“lsm330dlc_gyr”子系统
  • 位于event3节点下读写加速度传感器的“lsm330dlc_acc”子系统
  • 位于event1节点下读写红外线传感器的“gp2ap”子系统(魅族mx3是用红外线来测定光感和距离的)
  • 位于event5节点下读写屏幕触摸的“mx_ts”子系统
  • 位于event6节点下读写物理按键的“gpio-keys”子系统
  • 位于event7节点下读写耳机按键的“Headset”子系统(有些手机监控线控设备的系统常以hook为名,这里魅族使用不多见Headset来表示该类不知是否是有布局头戴式设备的意义)
  • 位于event2节点下读写罗盘的“compass”子系统

而mx3(不能说是Android哈这里针对mx3)就是从这些系统节点中读写设备的事件信息,以上信息我是在mx3灭屏时也就是按下电源键关闭屏幕后获取的,如果我们再次按下电源点亮屏幕,内核驱动就会不断地监控一些必要的读写事件,这里我们不想让我们的Terminal一直输出,使用getevent的-c参数设定最大的输出条数查看即可:




这里我设定了最大16条输出,亮屏后可见如上信息显示,如果不作输出限制,Terminal就会一直输出……也就是说加速度和红外线传感器的子系统会不断检测外部环境的变化,至于为什么,想想加速度感应和红外感应我想大家都应该能心知肚明。如果我们在getevent后在屏幕上快速Touch一下,那么event5节点下的子系统就回立即作出回应:



如上图中我们快速接触屏幕后得到的信息,可能不好懂对吧,给getevent加上-l参数格式化输出看看:




注:因为硬件设备、触摸区域力度、持续时间等因素的影响你的输出结果可能跟我不大一样,以具体你具体的输出为准,但输出信息大致是类似的。

这里拿第一条信息“/dev/input/event5: EV_ABS       ABS_MT_TRACKING_ID   000008e0”来说,其中/dev/input/event5上面我们说了表示设备节点;EV_ABS表示type事件类型;ABS_MT_TRACKING_ID表示code事件的扫描码;000008e0则表示具体的事件值。这些信息的定义都在kernel/include/linux/input.h文件中作出了声明,比如type输入设备类型包括如下这些:



  1. <font color="#444444"><font face="微软雅黑,"><font color="#000"><font face="Arial">#define EV_SYN                  0x00  
  2. #define EV_KEY                  0x01  
  3. #define EV_REL                  0x02  
  4. #define EV_ABS                  0x03  
  5. #define EV_MSC                  0x04  
  6. #define EV_LED                  0x11  
  7. #define EV_SND                  0x12  
  8. #define EV_REP                  0x14  
  9. #define EV_FF                   0x15  
  10. #define EV_PWR                  0x16  
  11. #define EV_FF_STATUS            0x17  
  12. #define EV_MAX                  0x1f </font></font></font></font>
复制代码

具体它们都代表什么就不多说了,都是些Linux的东西,一般来说比较常用的是EV_REL表示相对坐标类型、EV_ABS表示绝对坐标类型、EV_KEY表示物理键盘事件类型,EV_SYN表示同步事件类型等等,一个设备可以支持多个不同的事件类型而每个事件类型呢又可以设置不同的事件码,比如EV_SYN同步事件类型的事件码如下:



  1. <font color="#444444"><font face="微软雅黑,">#define SYN_REPORT      0   
  2. #define SYN_CONFIG      1   
  3. #define SYN_MT_REPORT       2  </font></font>
复制代码

其它的就不一一列举了都可以在input.h文件中找到相应的定义。上面图例中的一次快速触屏后的反馈信息可以做如下描述:



  1. <font color="#444444"><font face="微软雅黑,">/dev/input/event5: EV_ABS       ABS_MT_TRACKING_ID   000008e0 标志多点追踪信息的采集开始(需要设备支持)  
  2. /dev/input/event5: EV_ABS       ABS_MT_POSITION_X    00000280 上报接触面的中心点X坐标  
  3. /dev/input/event5: EV_ABS       ABS_MT_POSITION_Y    0000064b 上报接触面的中心点Y坐标  
  4. /dev/input/event5: EV_ABS       ABS_MT_PRESSURE      0000005b 上报手指压力  
  5. /dev/input/event5: EV_ABS       ABS_MT_TOUCH_MAJOR   00000014 上报主接触面长轴  
  6. /dev/input/event5: EV_SYN       SYN_REPORT           00000000 同步数据  
  7. /dev/input/event5: EV_ABS       ABS_MT_PRESSURE      00000057  
  8. 上报手指压力  
  9. /dev/input/event5: EV_ABS       ABS_MT_TOUCH_MAJOR   00000012  
  10. 上报主接触面长轴  
  11. /dev/input/event5: EV_SYN       SYN_REPORT           00000000 同步数据  
  12. /dev/input/event5: EV_ABS       ABS_MT_TRACKING_ID   ffffffff  
  13. 标志多点追踪信息的采集结束(需要设备支持)  
  14. /dev/input/event5: EV_SYN       SYN_REPORT           00000000 同步数据  </font></font>
复制代码

如上过程只是一次快速触碰所产生的节点读取,如果我们做出更复杂的手势操作比如多点切西瓜那样的效果尼玛光是采集这些信息都不得了!不过值得庆幸的是,对这些原始信息的采集用不着应用层的开发者来做,对于应用开发来说我们往往更关心一次事件是单击呢还是双击还是长按等等,而不是面对这些庞大而又复杂的原始信息,So,Android在获取到这些原始数据后会对其进行一定的转化便于使用,当然如果你需要做驱动开发涉及到这些原始数据的操作也可以直接获取其使用亦可。

可见,Linux中Input子系统对输入设备信息的捕获可以说是Android事件来源的老祖宗,当然这些玩意对于应用开发者来说没必要深入理解,仅作了解即可。文章开头我们曾这样说过,一次事件的源头必定来自于用户的交互,那么事实上是不是如此呢?早年打过游戏的童鞋肯定对按键精灵这玩意很熟悉吧,至少不陌生,我们使用按键精灵来模拟用户对键位的操作,也就是说我们并不一定需要用户真实的交互,模拟也行。同样地Android也给我们提供了另外一个很酷的工具sendevent来向/dev/input/写入事件信息模拟事件的产生,具体用法跟getevent很类似,就不多说了,自行尝试。


更多精华资源分享:

【强荐!】2016年最新Android学习路线图——按此路线图学习月薪过万{ 火速收入}

【新手连载】一:使用AndroidStudio搭建Android集成开发环境

【持续丰富中ing】java/android最最最核心知识点经典问题大集合<<<

[持续更新]2016最新Android视频教程+源码+技巧/经验+软件+面试


2 个回复

倒序浏览
受教了。。。。。。。。。。。。。。。
回复 使用道具 举报
多谢分享
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马