Android事件传递

来源:互联网 发布:2017年新出的网络红歌 编辑:程序博客网 时间:2024/06/03 14:53

一、事件写入
Android是linux内核的,Linux内核提供了一个Input子系统,Input子系统会在/dev/input/路径下创建硬件输入设备的节点,一般情况下在我们的手机中这些节点是以eventX来命名的,如event0,event1等等。由于这些设备节点是硬件相关的,所以每款设备都是不尽相同的。Android读取事件信息就是从/dev/input/目录下的设备节点中读取出来的,算是android事件处理的起点。物理设备产生触摸事件时,通过kernal将事件信息写入到/dev/input/下的设备节点中,获取事件只需要从该设备节点中读取即可。

二、创建连接
Android按键,触屏等事件是由WindowManagerService获取,并通过共享内存和管道的方式传递给ViewRoot,ViewRoot再分发给应用的View。下面看看WMS与ViewRoot如何创建管道连接。
事件传递系统中的管道的主要作用是在有事件被存储到共享内存中时,system_server端通知ViewRoot去读取事件的通信机制。既然是ViewRoot和system_server之间建立管道通信,那么ViewRoot和WindowManagerService(运行在system_server进程中)各需维护管道的一个文件描述符,其实ViewRoot和WindowManagerService不是各维护了一个管道的文件描述符,而是两个,实际上也就是ViewRoot和WindowManagerService之间实现了全双工的管道通信。WindowManagerService往ViewRoot方向的管道通信,表示WMS通知ViewRoot有新事件被写入到共享内存;ViewRoot往WindowManagerService方向的管道通信,表示ViewRoot已经消化完共享内存中的新事件,特此通知WMS。ViewRoot和WindowManagerService的管道的文件描述符都是被存储在一个名为InputChannel的类中,这个InputChannel类是管道通信的载体。
ViewRoot端建立管道

requestLayout();  mInputChannel = new InputChannel();  try {      res = sWindowSession.add(mWindow, mWindowAttributes,              getHostVisibility(), mAttachInfo.mContentInsets,              mInputChannel);  } catch (RemoteException e) {

在ViewRoot和WMS建立起连接之前首先会创建一个InputChannel对象,同样的WMS端也会创建一个InputChannel对象,不过WMS的创建过程是在ViewRoot调用add()方法时调用的。InputChannel的构造不做任何操作,所以在ViewRoot中创建InputChannel时尚未初始化,它的初始化过程是在调用WMS方法add()时进行的,看到上面代码中将mInputChannel作为参数传递给WMS,目的就是为了初始化。
再看WMS中InputChannel的初始化

if (outInputChannel != null) {      String name = win.makeInputChannelName();      InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);      win.mInputChannel = inputChannels[0];      inputChannels[1].transferToBinderOutParameter(outInputChannel);      mInputManager.registerInputChannel(win.mInputChannel);  }

上述代码主要工作其实就是创建一对InputChannel,这一对InputChannel中实现了一组全双工管道。在创建InputChannel对的同时,会申请共享内存,并向2个InputChannel对象中各自保存一个共享内存的文件描述符。InputChannel创建完成后,会将其中一个的native InputChannel 赋值给outInputChannel,也就是对ViewRoot端InputChannel对象的初始化,这样随着ViewRoot和WMS两端的InputChannel对象的创建,事件传输系统的管道通信也就建立了起来。如下图
这里写图片描述

WMS端的InputChannel会注册到InputManager中进行管理,InputManager启动了2个线程来管理事件发生与传递,InputReaderThread和InputDispatcherThread,InputReaderThread线程负责轮询事件发生; InputDispatcherThread负责dispatch事件。当input系统有事件发生时,会被InputReaderThread线程轮询到,InputReader会根据事件的device id来选择InputDevice,然后再根据事件的类型来选择InputDevice中的InputMapper,InputMapper会将事件信息通知给InputDispatcher;InputMapper的作用是将事件编码转换成事件类型。InputDispatcherThread对InputReader传递过来的事件进行dispatch前处理,比如确定focus window,特殊按键处理。WMS将事件传递给客户端的ViewRootImpl,WMS通过ViewRootImpl提供给WMS的Iwindow(ViewRootImpl的内部类)来与ViewRootImpl连接。

三、事件分发
当Touch事件传递到了ViewRootImpl中后,就会在View树中进行分发,视图树的层级结构应该比较清晰,在之前的文章中介绍过。在视图树中分发MotionEvent事件时,有三种对事件的处理:事件分发(dispatchTouchEvent方法),事件拦截(onInterceptTouchEvent方法(ViewGroup)),事件处理(onTouchEvent方法),在这儿就不追踪源码了。