Android输入事件流程中的EventHub分析及源码演示

时间:2022-01-08 19:46:35


Android2.3的输入事件流程与以前版本有了较大的不同,这里做一下详细的分析,最后我把自己分析时用的演示代码放在了这里:

http://code.google.com/p/flying-on-android/

下面的分析都是基于这些源码的,大家可以下载下来一边看源码一边看文档。源码里只要关注FlyingEvent这个类就可以了。如果只想看一下演示结果,可以直接把包里的flying放到机器的/system/bin目录执行,打开logcat后就可以看到演示输出。运行程序时,机器屏幕会有异象产生,很正常,因为这个程序原本是用于显示SurfaceFlinger的,这次为了演示EventHub稍微改了一下。大家只要关注FlyingEvent.cpp这个文件就好了。

大家也可以用源码自己编译出演示程序,只要把解压后的flying文件夹放到/frameworks/base/cmds/目录下,然后切换到flying目录下使用mm编译。

 

先大致介绍一下整个流程,再做重点分析。输入事件流程一共涉及到下面这几个文件:

/frameworks/base/services/java/com/android/server/WindowManagerService.java

/frameworks/base/services/java/com/android/server/InputManager.java

/frameworks/base/services/jni/com_android_server_InputManager.cpp

/frameworks/base/libs/ui/InputReader.cpp

/frameworks/base/libs/ui/InputDispatcher.cpp

/frameworks/base/libs/ui/EventHub.cpp

其中,WindowManagerService.java和InputManager.java主要向Android为窗口系统提供服务,EventHub.cpp主要用来读取设备文件中的RawEvent,而InputReader.cpp和InputDispatcher.cpp算是它们之间的对接层。

 

它们的关系是:WindowManagerService通过InputManager提供的接口开启一个线程驱动InputReader不断地从/dev/input/目录下面的设备文件读取事件,然后通过InputDispatcher分发给连接到WindowManagerService服务的客户端。

InputReader从设备文件中读取的是RawEvent,在交给InputDispatcher进行分发之前,它需要先把RawEvent进行转化分类,拆分成KeyEvent、MotionEvent、TrackEvent各种类型等。这篇文章主要关注的就是这个RawEvent的拆分过程,所以我们的重点在EventHub.cpp中。并且,为了简单化分析过程,在这里我的分析只关注触摸屏事件。看它是如何从RawEvent被拆分成应用层用户事件MotionEvent的。

 

看下面的分析之前,最好先去上面提到的地址把源码下载下来,参照里面的FlyingEvent.cpp。

 

整个过程大致分成这么几步:

一、初始化。

先new一个EventHub的实例:mEventHub(new EventHub),

接下来,开启一个线程通过mEventHub不停地从设备文件中读取RawEvent并处理:

while (1) {

    RawEvent event;

    mEventHub->getEvent(&event);

    process(event);

}

EventHub在初始化的时候做一些事情,

1、搜索当前的输入设备每搜索到一个就会产生一个类型为DEVICE_ADDED的事件,当读取这种RawEvent时,InputReader会把搜索到的这个设备记录下来。

2、如果搜索到了键盘时,就会加载键盘布局文件。加载完成后产生一个类型为FINISHED_DEVICE_SCAN的事件。这样,后边从驱动读取用户按键时,就会去加载的键盘布局文件中寻找映射的键值封装成KeyEvent返回给用户。

 

二、EventHub初始化完毕后,就开始等待用户输入。线程一直阻塞在mEventHub->getEvent(&event),直到有用户事件产生才会返回。

当有一个事件产生时,传递给process进行处理。

 

三、事件拆分

FlyingEvent.process里面主要调用了FlyingEvent.consume方法来处理用户事件。这里只分析touch事件。touch事件可以分为三种:down,move,up。

down类型的touch事件需要四个RawEvent来完成,第一个是X坐标(ABS_X),第二个是Y坐标(ABS_Y),第三个代表方向(ABS_PRESSURE)(0的时候是up,1的时候是down,所以这里应该是1),第四个是结束标志(SYN_REPORT)。

move类型的touch事件需要三个RawEvent来完成,第一个是X坐标,第二个是Y坐标,第三个是结束标志。

up类型的touch事件需要两个RawEvent来完成,第一个代表方向(0的时候是up,1的时候是down,所以这里应该是0),第四个是结束标志。

可能你已经注意到了up事件是没有坐标信息的,它的坐标信息与down(没有move时)或最后一个move(down和up之间有move事件产生)事件的坐标相同。

 

从FlyingEvent.consume方法中,每一个事件最终都会生成一个TouchEvent,然后调用printTouchEvent进行打印,最后把它存储到eventBuffer中。

 

 

参考文章

李先静的“Android输入事件流程“,不过使用的Android版本比较老了。

http://blog.csdn.net/absurd/archive/2009/05/17/4195363.aspx