https://www.cnblogs.com/lcw/p/3373214.html
1. Android 事件传递
Android 事件传递的流程,按键,触屏等事件 由WindowManagerServcie 获取,并通过共享内存和管道的方式传递给ViewRoot,ViewRoot再dispatch 给Application 的View。当有事件从硬件设备输入时,system_server端在检测到事件发生时,通过管道(pipe)通知ViewRoot事件发生,此时ViewRoot 再去内存中读取这个事件信息。
为什么使用共享内存而不是直接使用Binder机制?
Google 为了保证事件响应的实时性,在选择进程间传递事件的方式中,选择了共享内存的方式。 由于共享内存在数据管理过程中基本不涉及到内存的数据拷贝,只是在进程读写时涉及到2次数据拷贝,这个是不可避免的数据拷贝,因此这种方式能够很好的保证系统对事件的响应,但是仅仅是共享内存是不够的,因为共享内存的通信方式并不能够通知对方有数据更新,因此android 在事件处理过程中加入了另一种进程间通信方式 管道(pipe)。
管道的效率不如共享内存高,会不会影响事件处理的实时性?
没关系,每次system_serve 通知ViewRoot 只是向其传递一个字符,既轻巧又简单。
事件处理系统的初始化过程
Android 事件传递系统是以共享内存和管道的进程间通信方式来实现传递的。
创建管道连接
管道的主要作用是在有事件被存储到共享内存中时,system_server 端通知ViewRoot去读取事件的通信机制。
ViewRoot 和 WindowManagerService (负责事件传递,运行在system_server进程中) 各需维护管道的一个文件描述符。实现全双工的管道通信。 WindowManagerService —> ViewRoot 方向的管道通信,表示WMS通知ViewRoot 有新事件被写入到共享内存。 ViewRoot --> WindowManagerService 方向的管道通信,表示ViewRoot 已经消化完共享内存中的新事件,特此通知WMS。 ViewRoot 和 WindowManagerService 的管道的文件描述符都是被存储在一个名为InputChannel 的类中,这个InputChannel 类是管道通信的载体。
在创建InputChannel 对的同时,会申请共享内存,并向2个InputChannel 对象中各自保存一个共享内存的文件描述符。
一个管道通信只是对应一个Activity 的事件处理,也就是当前系统中有多少个Activity 就会有多少个全双工管道。 此时系统需要一个管理者来管理以及调度每一个管道通信 因此在创建完InputChannel 对象后,需要将其注册到这个管理者中去。
ViewRoot 端的一般情况下会注册到一个NativeInputQueue 对象中 WMS 端注册在InputManager对象中。
注册到NativeInputQueue ViewRoot 端InputChannel 对象在向NativeInputQueue注册时,需要注册3个参数:
将InputChannel 对象对应的Native InputChannel 传递给 NativeInputQueue; 将ViewRoot 的成员变量InputHandler 传递给NativeInputQueue,这个InputHandler则是事件的处理函数,传递它的主要作用是明确当前ViewRoot的事件处理函数。 当前Application 的主进程的 MessageQueue
Android 在实现事件传输时,很大程度上借用了线程Looper 和 MessageQueue 的轮询(poll ) 机制,通过它的轮询机制来检测管道上是否有消息通知事件发生,借用Looper机制能够很大限度的保证事件能够第一时间被Application知晓。
注册过程中,android 会将InputChannel 对象中保存的管道的文件描述符交给MessageQueue 的 native looper 去监听,同时向native looper 指示一个回调函数,一旦有事件发生,native looper 就会检测到管道上的数据,同时会去调用指示的回调函数。 handleReceviceCallback()@android_view_InputQueue.cpp
NativeInputQueue 对象,真个系统只有一个,它为了负责管理这么多的Application的事件传递 android 在NativeInputQueue 类中定义了一个子类Connection,每个InputChannel 对象在注册时都会创建一个自己的Connection对象。 registerInputChannel()@android_view_InputQueue.cpp
注册到InputManager
不采用Looper 来轮询是否有事件发生,InputManager 启动了2个进程来管理事件发生与传递, InputReaderThread 和 InputDispatcherThead, InputReaderThread进程负责轮询事件发生; InputDispatcherThread 负责dispatch 事件。(如果用一个进程来管理,在轮询input 系统event的事件间隔会变长,有可能丢失事件)。
虽然没有使用Looper 来轮询事件的发生,但是InputDispatcher 使用了native looper(并不是WMS线程的,而是线程InputDispatcher自定义的) 来轮询检查管道通信,这个管道通信表示InputQueue是否消化完成dispatch 过去的事件。
WMS 在初始化时会创建这么一个InputManager 实例,是系统唯一的。 JAVA 层的InputManager 实例并没有实现太多的业务,真正实现Input Manager 业务是Native的 NatuveInputManager实例,它在被创建时,建立起了整个WMS端事件传递系统的静态逻辑。
InputDispatcher 数据传输管理的核心业务是在InputDispatcher中完成的,因此最终WMS端InputChannel 对象会注册到InputDispatcher 中,同样的由于整个系统中InputDispatcher 实例只有一个,而WMS 端InputChannel对象是和ViewRoot 一一对应的。 InputDispatcher 中的Connect类的核心业务是由InputPublisher 对象来实现的,该对象负责将发生的事件信息写入到共享内存。
Notify InputDispatcher表示不同的事件通知InputDispatcher的函数调用,这几个函数虽然是被InputReaderThread调用的,单却是在InputDispatcher定义的。
InputDispatcherThread线程操作 InputDispatcherThread线程的轮询过程dispatchOnce()–>dispatchOnceInnerLocked()。 InputDispatcherThread线程不停的执行该操作,以达到轮询的目的,重点也就是这2个函数处理了。 InputDispatcherThread基本流程 InputDispatcherThread的主要操作是分两块同时进行的, 一部分是对InputReader传递过来的事件进行dispatch前处理,比如确定focus window,特殊按键处理如HOME/ENDCALL等,在预处理完成 后,InputDispatcher会将事件存储到对应的focus window的outBoundQueue,这个outBoundQueue队列是InputDispatcher::Connection的成员函数,因此它是和ViewRoot相关的。 一部分是对looper的轮询,这个轮询过程是检查NativeInputQueue是否处理完成上一个事件,如果NativeInputQueue处理完成事件,它就会向通过管道向InputDispatcher发送消息指示consume完成,只有NativeInputQueue consume完成一个事件,InputDispatcher才会向共享内存写入另一个事件。
事件传递
InputReaderThread 线程操作 当input系统有事件发生时,会被InputReaderThread 线程轮询到 InputReader 会根据事件的device id 来选择的InputDevice 然后再根据事件的类型来选择InputDevice中的InputMapper,InputMapper会将事件信息通知给InputDispatcher
总结输入事件的处理流程
首先从Kernel 传递上来的键值由EventHub进行转码,之后由InputReader将其解释成各个事件,再由InputDispatcher分发。 InputManager是InputReader和InputDispatcher 线程的创建者,它只有一个职责,就是被WindowManagerServic e使用,从Native层获取按键事件。
WindowManagerService 则负责与窗口对接,分发按键消息。
源码位置
事件分发给最前面的窗口: /frameworks/base/services/java/com/android/server/WindowManagerService.java 拦截消息的处理类: /frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java 按键事件定义: /frameworks/base/core/java/android/view/KeyEvent.java Java层输入管理: /frameworks/base/services/java/com/android/server/InputManager.Java native层输入管理: /frameworks/base/libs/ui/InputManager.cpp 事件读取线程: /frameworks/base/libs/ui/InputReader.cpp 事件分发线程: /frameworks/base/libs/ui/InputDispatcher.cpp 键码与键值转换: /frameworks/base/libs/ui/EventHub.cpp
|