关于Android的事件分发机制,网上的文章快烂大街了, 大多数文章都是在千篇一律的讲activity, viewgroup, view之间的分发机制, 对于事件的源头却没有提及. 比如, 当手指触碰屏幕的一瞬间, 手机是怎么知道手指触碰的哪里, 是触碰的back键, 还是home键, 还是哪个坐标点, 是点击事件, 还是滑动事件等等, 也就是说, Android系统的事件采集是怎样的. 接下来是怎么把这些采集的事件信息进行处理的, 由谁来处理, 处理完成后又是由谁最后分发给到具体的activity的?
有人可能会说, 作为一个普通的Android应用层开发者, 不需要掌握这些也可以啊, 我不想知道事件是怎么来的, 我只知道事件是怎么分发的, 我能够在程序中控制事件就可以了. 的确, 事实是这样, 不了解这些不影响应用层的开发, 但是如果掌握了这些, 那就太有意思了, 可以做很多有意思的事情, 比如说, 按键精灵类的app为什么能够记忆你的操作, 而且还能够自动模拟你的操作而解放你的双手? 其实, 这类app的原理无非就是通过某个办法来采集到你的所有的操作过程, 即事件采集, 这样他就记忆下来了, 当需要自动模拟事件的时候, 它会把这些采集到的信息经过处理, 转变成可执行信息,这样就能够实现模拟人的双手来进行自动模拟操作了.
因为要想讲清楚整个事件的采集,处理及分发过程, 篇幅过大, 因此准备用一个系列文章来讲, 本文是系列文章的开篇, 那么先讲采集.
(一) 原始事件信息
打开模拟器, 或者用usb把手机连接上电脑并打开手机上面的开发者模式, (这里为了截图方便用了模拟器)打开cmd, 输入adb shell getevent回车, 然后点击模拟器或者手机后, 就会看到有N多行的/dev/input/event......输出. 其实, 这些信息就是最原始的事件信息.
(注:严格来说最原始的信息肯定是由硬件捕获到的, 这里所说的原始信息是经过硬件处理后返回给framework层的信息)
再看一下, 我们能否通过cmd命令来控制手机来模拟操作事件呢? 重新打开cmd, 或者Ctrl + c退出刚才的命令, 重新执行adb shell input keyevent 4 , 发现手机自动执行了返回键
(二) 信息分析
以上简单的演示了下采集信息和发送信息, 接下来开始解析信息.cmd重新执行adb shell getevent -t -l, 再次点击手机的某个app, 显示出的信息是这样的:
前面[ 13236.364793]显示的是时间, 是手机或模拟器开机后到命令执行时的时间间隔, 后面显示的是具体命令, 其中, /dev/input/event1表示的是屏幕的输入事件, 第一个ABS_MT_TRACKING_ID 表示采集信息开始, 后一个ABS_MT_TRACKING_ID表示采集信息结束, ABS_MT_PRESSURE表示的是屏幕感受到的压力值, SYN_REPORT 表示的是同步数据, 最重要的是ABS_MT_POSITION_X和ABS_MT_POSITION_Y, 毫无疑问, 这个就是表示屏幕感受到的触碰坐标位置. 最右边的一列是16进制的值.
其他的信息我们可以先忽略, 最主要的是要记录时间和坐标点, 接下来就是把采集到的坐标点转换成10进制的坐标点, x坐标采集到的值是00005487,转换10进制是21639, 同理, y坐标转换前是00006b5b, 转换后是27483, 接下来需要根据公式来获取到真正的应用层能识别的坐标系, 具体公式为:
x = (x-xmin) * 手机像素宽 / (xmax-xmin) ;
y = (y-ymin) * 手机像素高 / (ymax-ymin);
手机像素我们可以通过代码获取当前手机的像素, 但是xmin和xmax, 以及ymin和ymax是什么呢?我们还是通过cmd用adb shell getevent -p命令来获取. 执行命令后,拉到最下方, 看到如下图示:
我们找到0035和0036的行, 即
0035 : value 0, min 0, max 32767, fuzz 0, flat 0, resolution 0
0036 : value 0, min 0, max 32767, fuzz 0, flat 0, resolution 0
0035所在的行就是x信息, 0036所在的行就是y信息, 每行的min值和max值对应上面公式的min和max, 所以我这里最终转换成的x, y坐标结果如下: (我的模拟器经过代码获取的宽高分辨率分别是480, 728)
x = (21639 - 0) * 480 / (32767 - 0) = 317
y = (27483 - 0) * 728 / (32767 - 0) = 610
即当点击手机桌面屏幕的(317, 610)坐标, 就打开了这个app, 那么我们接下来验证一下, 通过cmd来发送命令, 来模拟点击事件, 看看模拟器或者手机是不是能打开这个app呢?
重新打开cmd, 执行adb shell input tap 317 610命令, 结果如下:
我们成功的通过命令来模拟点击桌面应用了. 其实整个过程就是一个采集信息,处理信息,发送信息的过程。
到这里, 其实我们就可以自己开发出来一个类似按键精灵类的简单版app了, 大体思路就是在app中给一个按钮,用来触发信息采集, 之后用户的一切触碰屏幕操作都进行录入, 再给一个结束采集的按钮, 触发按钮就结束信息录入, 并开始处理信息, 包括每个动作的属性(是点击, 还是滑动, 还是长按等等), 相邻动作的时间间隔, 等等, 然后保存起来, 最后再给一个自动模拟事件的按钮, 当触发时, 程序开始自动读取保存的事件信息并执行模拟事件了.
以上, 是我们人为的进行事件采集, 处理, 发送. 那么Android系统内部是怎么做的呢? 是谁负责把这个事件信息处理后转换成为各种KeyEvent或者MotionEvent的, 以及最终是怎么传递到的activity中的, 请关注系列文章之后的文章。