当前位置:操作系统 > 安卓/Android >>

android的窗口机制分析------事件处理

  由于Android是linux内核的,所以它的事件处理也在linux的基础上完成的,因此本文我们从linux 内核往应用这个方向慢慢理清它的处理过程。

    linux内核提供了一个Input子系统来实现的,Input子系统会在/dev/input/路径下创建我们硬件输入设备的节点,一般情况下在我们的手机中这些节点是以eventXX来命名的,如event0,event1等等,但是如果是虚拟机的话,我们可以看到一个mice,这个mice代表鼠标设备,这是由于PC需要使用鼠标来模拟触屏。由于这些设备节点是硬件相关的,所以每款设备都是不尽相同的。看到了这些输入的设备节点,我们可能比较困惑这些eventXX到底代表什么含义呢,也就是说到底是什么样的设备创建了这个节点呢?我们可以从/proc/bus/input/devices中读出eventXX相关的硬件设备,这里具体的就不多说了,我们只需要知道android读取事件信息就是从/dev/input/目录下的设备节点中读取出来的,算是android事件处理的起源吧,可以让大家知道按键、触屏等事件是从哪里来的,不是我们的重点。

    首先,简而言之的介绍一下android事件传递的流程,按键,触屏等事件是经由WindowManagerService获取,并通过共享内存和管道的方式传递给ViewRoot,ViewRoot再dispatch给Application的View。当有事件从硬件设备输入时,system_server端在检测到事件发生时,通过管道(pipe)通知ViewRoot事件发生,此时ViewRoot再去的内存中读取这个事件信息。

    至于android在事件处理上为什么使用共享内存而不是直接使用Binder机制,我的猜测应该是google为了保证事件响应的实时性,因此在选择进程间传递事件的方式中,选择了高的共享内存的方式,由于共享内存在数据管理过程中基本不涉及到内存的数据拷贝,只是在进程读写时涉及到2次数据拷贝,这个是不可避免的数据拷贝,因此这种方式能够很好的保证系统对事件的响应,但是仅仅是共享内存是不够的,因为共享内存的通信方式并不能够通知对方有数据更新,因此android在事件处理过程中加入了另一种进程间通信方式管道(pipe),管道的效率不如共享内存高,会不会影响事件处理的实时性?没关系,每次system_serve通知ViewRoot只是向其传递一个字符,即轻巧有简单,一个字符的多次数据拷贝,我想google还是能够接受的。

    好的,了解了一些基本知识后,我们从底层往上层来分析事件的传递过程,这里为了下文便于理解,首先列出整个事件处理的结构图。

    \

1. 事件处理系统的初始化过程

    前文讲到android的事件处理系统,这里称为事件传递系统更贴切一些,因为android事件系统中比较复杂就是其传递过程,下面我们就以事件传递系统来代替事件处理系统。android事件传递系统是以共享内存和管道的进程间通信方式来实现传递的,为了便于理解它的传递机制,事件传递系统的初始化工作的理解则会显得非常的重要

 

    1.1 创建管道连接

    事件传递系统中的管道的主要作用是在有事件被存储到共享内存中时,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端的管道的建立。

 

    setView()@ViewRoot.java

 

 

 

 

 

view plainrequestLayout(); 

mInputChannel = new InputChannel(); 

try { 

    res = sWindowSession.add(mWindow, mWindowAttributes, 

            getHostVisibility(), mAttachInfo.mContentInsets, 

            mInputChannel); 

} catch (RemoteException e) { 

 

 

 

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

 

    addWindow()@WindowManagerService.java

 

 

 

 

 

view plainif (outInputChannel != null) { 

    String name = win.makeInputChannelName(); 

    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); 

    win.mInputChannel = inputChannels[0]; 

    inputChannels[1].transferToBinderOutParameter(outInputChannel); 

     

    mInputManager.registerInputChannel(win.mInputChannel); 

}   

 

 

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

    创建InputChannel pair的过程以及管道建立,共享内存申请的过程就不再列出它的代码了,请参考openInputChannelPair()@InputTransport.cpp。下图为ViewRoot和WMS两端创建InputChannel pair之后的结构。

    \

    

    1.2 InputChannel的注册过程

    上一节介绍了InputChannel对象的创建过程,这个过程将管道通信建立了起来,但是我们需要清楚的一点是,一个管道通信只是对应一个Activity的事件处理,也就是当前系统中有多少个Activity就会有多少个全双工管道,那么系统需要一个管理者来管理以及调度每一个管道通信,因此我们在创建完InputChannel对象后,需要将其注册到这个管理者中去。

    明白了InputChannel对象需要注册的原因之后,我们再看ViewRoot和WMS端的InputChannel对象各自需要注册到哪里?其实也很好理解,两个InputChannel对象WMS端的是管道通信的sender, ViewRoot端的是Receiver(尽管创建的全双工,但是目前只使用到了它的一向的通信,另一方向的通信尚未使用),那么着两个InputChannel对象肯定需要被两个不同的管理者来管理。ViewRoot端的一般情况下会注册到一个NativeInputQueue对象中(这是一个Native的对象,而JAVA端的InputQueue类仅仅是提供了一些static方法与NativeInputQueue通信),只要当用到NativeActivity时,会是另外一种处理机制,这里我们不管它,NativeActivity毕竟很少用到;WMS端注册在InputManager对象中。其实从NativeInputQueue和InputManager的名字中也就能知道各自的功能了。

    1.2.1 注册到NativeInputQueue

    ViewRoot端InputChannel对象在向NativeInputQueue注册时,需

补充:移动开发 , Android ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,