androd输入管理系统机制解析
android的输入管理系统主要完成按键、触摸板、鼠标等输入设备的事件输入,功能包括,输入设备的事件输入及向焦点窗口和焦点视图的事件派发,事件的插入,事件的过滤,事件的拦截等功能。
整个输入系统包括服务端和客户端两部分,服务端部分主要完成输入设备事件的读取、事件的映射、事件的插入、事件的过滤、事件的拦截等功能;客户端部分主要完成事件向焦点窗口和焦点视图的派发。
输入系统的整个架构采用的是管道过滤器模式(Pipe and Filter)架构模式。服务端的InputReader和InputDispatcher对象及客户端的InputEventReceiver对象及InputConsumer对象对应着过滤器构件,具有各自的输入、处理、输出单元,两者通过管道连接到一起。
下图是ANDROD4.4 版本的服务端的系统类图,4.4 版本的输入系统相对于4.0版本作了如下较大改动:输入管理服务从窗口管理服务中独立了出来, C++ 实现部分结构也作了优化,其各个模块部分之间采用了接口进行交互,包括输入事件读取接口InputReaderInterface、事件监听和提交接口InputListenerInterface和InputDispatcherInterface,以及原始事件接收接口EventHubInterface。
图中上面部分为JAVA部分对应的类,主要是一个输入管理服务InputManagerService和客户端的事件接收和事件派发类,下面部分为C++本地实现的部分,C++部分主要由InputReader、InputDispatcher两个过滤器构件和线程及辅助对象构成,两个过滤器构件的任务在对应的两个线程InputReaderThread和InputDispatcherThread内运行。
InputReader类是InputReaderInterface接口的实现,用来实现事件的读取功能。 InputDispatcher类是InputDispatcherInterface接口的实现类, 而InputDispatcherInterface 接口又是InputListenerInterface接口的派生接口,用来实现输入事件的监听和提交。
整个流程包括事件读取流程和事件提交两个流程,分别运行在InputReaderThread和InputDispatcherThread两个线程内。在输入读取线程内,InputReaderInterface接口对象通过EventHubInterface接口从驱动读取原始输入事件,经过事件映射后通过InputListenerInterface接口提交给InputDispatcher对象,在InputDispatcherThread线程内由对象InputDispatcher对象、InputPublisher
对象共同负责事件的提交,最后通过InputChannel对象发送输入事件到服务端的管道内,由客户端通过InputConsumer从管道的另一端读取事件。
InputReader、InputDispatcher对象以及InputReaderThread和InputDispatcherThread两个线程对象都由本地InputManager类创建和启动,而本地InputManager类则有上面框架中的InputManagerService通过JNI进行实例化,在其nativeInit
JNI接口中创建一个NativeInputManager对象作为JAVA层与本地层的交互对象,NativeInputManager对象实例化时创建本地InputManager对象和EventHub对象。
输入系统管道的打开及服务端事件提交管道的注册则在WindowManagerService调用addWindow函数新建窗口时创建和注册;而客户端事件输入管道的注册由客户端ViewRootImpl对象调用SetView时注册。因此输入系统服务端运行在输入管理服务进程内,客户端运行在应用程序所在进程内。
一、 输入系统服务端事件读取和派发流程(正常情况)
整个流程由InputReaderThread线程触发,在线程中调用InputReader对象的loopOnce函数,在loopOnce函数中首先通过EventHubInterface接口的getEvents函数读取输入设备事件,经过处理后从事件中获得对应的deviceId,根据deviceId在mDevices数组中找到对应的输入设备对象或者构造新的InputDevice对象,然后调用InputDevice的process函数。
在InputDevice的process函数中对每个事件由InputDevice对象的mMappers数组中登记的每个InputMapper对象依次处理,分别调用其process函数,对原始输入事件进行映射。
不同的事件类型采用了不同的具体InputMapper对象进行映射,如按键事件对应的是KeyboadInputMapper对象,另外还有TouchInputMapper、CursorInputMapper、VibratorInputMapper、SwitchInputMapper、JoystickInputMapper等映射对象,这些对象的类都是InputMapper虚拟类的具体类。
一个支持多种类型的事件输入的输入设备,需要使用多个不同的输入映射对象分别进行映射。
在InputMapper对象的process函数中把事件封装成NotifyArgs对象,然后调用InputMapper的监听对象QueedInputListener的相关事件监听接口函数,如notifyKey函数。
在QueedInputListener的监听接口函数中作为参数传进来的NotifyArgs对象放入QueedInputListener的事件队列ArgsQueue中,然后返回并在loopOnce函数中调用QueuedListener对象的flush函数。
在flush函数中依次调用ArgsQueue队列中由事件封装成的NotifyArgs对象的notify函数,notify函数的参数也是一个监听对象,在QueuedInputListener对象实例化时赋值,对应的是一个InputDispatcher对象。NotifyArgs对象的notify函数调用其参数引用对象(InputDispatcher)的事件通知函数,以NotifyArgs对象为参数,如按键事件对应的notifyKey。
InputDispatcher对象的事件通知函数根据作为参数传进来的事件构造一个EventEntry对象,然后调用enqueueInboundEventLocked函数把构造的EventEntry事件对象放入内部事件队列中(mInboundQueue),最后调用Looper的wake函数,唤醒事件提交线程即InputDispatcherThread线程来进行事件向管道中的提交。
下面是InputDispatcherThread线程唤醒后的输入事件提交流程图:
InputReader读取的输入事件作为参数通过InputListenerInterface的接口函数被InputDispatcher接收。接收的事件构造为EventEntry对象放入InputDispatcher对象的EventEntry类型的队列mInboundQueue中,并唤醒InputDispatcherThread线程。
InputDispatcherThread线程不断从该队列中读取输入事件。首先调用InputDispatcher对象的dispatchOnce函数;dispatchOnce函数又调用dispatchOnceInnerLocked函数,在dispatchOnceInnerLocked函数中进行一系列判断后若是按键事件则调用dispatchKeyLocked函数;
在dispatchKeyLocked函数中使用findFocusedWindowTargetsLocked函数寻找焦点窗口,并把找到的焦点窗口通过调用函数addWindowTargetLocked放入mCurrentInputTargets数组中,然后调用addMonitoringTargetsLocked为刚才找到的焦点窗口绑定一个inputChannel通道,接着调用dispatchEventLocked函数;
在dispatchEventLocked函数中首先根据刚才焦点窗口绑定的inputChannel找到对应的一个Connection对象,然后调用prepareDispatchCycleLocked;
在prepareDispatchCycleLocked函数中调用enqueueDispatchEntryLocked函数,在enqueueDispatchEntryLocked函数中根据传进来的事件对应的对象eventEntry构造一个 DispatchEntry对象,DispatchEntry对象中包含inputTarget的信息,并放入connection对象outboundQueue队列;接着又调用startDispatchCycleLocked;
在startDispatchCycleLocked函数中通过connection对象中的inputPublisher对象引用调用inputPublisher对象的publishKeyEvent函数或publishMotionEvent函数向客户端发送事件,到这里就完成了服务端的输入事件处理。
服务端的输入事件处理流程中主要采用了Observer模式(也可称为监听者模式)和命令模式以及策略模式,如QueuedListener对象是InputMapper对象的监听对象,两个类之间的关系构成Observer模式;
InputMapper对象本身就是设备事件映射策略的实现,InputMapper是一个虚拟类,共有六个派生类,在InputReader对象的createDeviceLocked函数中根据设备的类型实例化不同的InputMapper的派生类,如KEYBOARD_TYPE对应的是KeyboardInputMapper;
而在InputMapper对象向InputDispatcher对象转发事件过程中采用了命令模式,模式类图如下:
二、输入系统客户端事件派发过程
相对于4.0以前的版本,4.4 版本的客户端向视图的事件派发机制也作了大的改动,如上图所示:客户端事件的派发任务主要由ViewRootImpl类承担,在ViewRootImpl类中添加了几个内部事件处理对象(InputStage的派生类),事件接收机制也更加有效。整个事件接收和派发流程如下:
客户端的事件接收通过InputEventReceiver对象(具体为ViewRootImpl在SetView函数中实例化的一个WindowInputEventReceiver类型的对象)和对应的本地NativeInputEventReceiver对象共同完成。
NativeInputEventReceiver对象在收到输入系统产生的输入事件时,通过jni 调用InputEventReceiver对象的dispatchInputEvent接口向JAVA传送底层来的输入事件,InputEventReceiver对象的dispatchInputEvent接口又调用其onInputEvent接口,在onInputEvent接口函数中调用ViewRootImpl对象的enqueueInputEvent函数放入ViewRootImpl对象的事件队列(QueuedInputEvent)中。然后调用doProcessInputEvents函数立即对队列中的事件进行事件处理或者调用scheduleProcessInputEvents来异步处理事件。
在 doProcessInputEvents函数调用deliverInputEvent函数来提交队列中的事件,在deliverInputEvent函数中调用InputStage对象的deliver函数在一个事件处理责任链中传递和处理事件。
如类图所示,NativePreImeInputStage、ViewPreImeInputStage、ImeInputStage、EarlyPostImeInputStage、NativePostImeInputStage、ViewPostImeInputStage、SyntheticInputStage构成一个输入事件责任处理链,如果本阶段对事件没有处理,则传递到下一个对象进行处理,直至事件被处理。NativePreImeInputStage、ViewPreImeInputStage、ImeInputStage三个类用来实现输入法的按键派发和处理,如果事件不传递到输入法服务中,这三个类可以跳过,直接从EarlyPostImeInputStage对象开始处理,在ViewPostImeInputStage对象处理阶段调用了主View
对象(对应PhoneWindow中的DecorView对象)的事件提交函数如(dispatchKeyEvent)函数向视图对象提交输入事件,在当前窗口的视图树中派发事件。派发完后返回进行其它处理如聚焦切换等;
主视图的dispatchKeyEvent函数首先调用dispatchKeyShortcutEvent及performPanelShortcut函数进行快捷键处理,然后调用getCallback返回视图的回调对象,ACTIVITY实现了
Window.Callback,因此如果主视图绑定有ACTIVITY,则getCallback返回主视图邦定的ACTIVITY对象,则主视图的dispatchKeyEvent函数继续调用ACTIVITY对象的dispatchKeyEvent函数进行事件派发,否则调用其父类的dispatchKeyEvent函数直接在视图树上向上传递事件。
在ACTIVITY的dispatchKeyEvent函数中首先通过getWindow函数获得ACTIVITY对象的Window,然后调用Window的superDispatchKeyEvent在视图树中派发事件,派发到各个焦点ViewGroup和Focuse View;如果视图树没有处理事件则调用event.dispatch函数派发事件由ACTIVITY对象本身处理,ACTIVITY对象的事件回调接口在这里被调用;
一步造成PhoneWindow的superDispatchKeyEvent函数被调用,其实际又调用的是主视图DecorView的superDispatchKeyEvent函数;DecorView的superDispatchKeyEvent函数调用其父类的dispatchKeyEvent函数,由于DecorView继承于ViewGroup,因此最终ViewGroup的dispatchKeyEvent函数被调用;
ViewGroup的dispatchKeyEvent函数首先调用其父类的dispatchKeyEvent函数向父类视图派发事件;父类的视图没有处理事件,再通过ViewGroup对象的mFocused(焦点子视图)成员向下一级焦点视图派发事件。这样一级级向各个焦点ViewGroup和Focuse View视图派发事件,完成整个视图树的事件派发。
如果ACTIVITY的dispatchKeyEvent函数和当前窗口的视图树都没有处理事件,则根据按键状态调用PhoneWindow的onKeyDown函数或onKeyUp函数。
以上事件在视图树的派发也是职责链模式(Chain of Responsibility)的采用,根据构成窗口的视图树完成事件的派发。
由服务端的inputPublisher对象和客户端的inputConsumer对象还共同构成了生产/消费者模式,通过inputPublisher对象发布事件, inputConsumer对象消费和读取事件。
三、事件的过滤和插入及事件的拦截流程
事件的过滤和拦截采用了策略模式机制进行不同的事件处理,由类图中的InputManagerService、InputFilter、PhoneWindowManager、WindowManagerCallback、InputMonitor等JAVA对象共同完成事件的过滤和拦截;WindowManagerCallback、IInputMonitor主要负责回调事件的转发,IInputFilter接口是事件过滤请求的处理接口;应用可以通过InputManager对象的injectInputEvent函数完成事件的插入;PhoneWindowManager对象最终负责派发事件的拦截。事件的过滤请求和拦截请求流程相似,只是目的地不一样。
事件的插入流程如下:InputManager对象的injectInputEvent函数调用InputManagerService服务的同名injectInputEvent函数,InputManagerService服务的injectInputEvent函数通过JNI调用InputDispatcher的injectInputEvent函数,在InputDispatcher的injectInputEvent函数中首先进行一些权限判断,然后根据注入的事件类型构造一个插入事件,如按键对应的KeyEntry对象,然后通过enqueueInboundEventLocked函数把插入事件注入mInboundQueue队列,然后唤醒派发流程,事件注入完成。
过滤请求和拦截请求流程都由InputDispatche对象发起,并通过InputDispatcherPolicyInterface接口函数向JAVA层传递请求。
InputDispatcherPolicyInterface接口有几个输入事件拦截回调接口函数(如按键事件对应的 interceptKeyBeforeQueueing、interceptKeyBeforeDispatching事件拦截接口),用来向java层传递事件队列前拦截请求和事件提交前拦截请求。InputDispatcherPolicyInterface接口还有一个事件过滤接口filterInputEvent
函数用来传递向java 层过滤请求。
NativeInputManager对象是InputDispatcherPolicyInterface接口的实现对象,而在InputDispatcher对象中则有一个指向NativeInputManager对象的引用字段mPolicy,用来在事件提交线程中通过InputDispatcherPolicyInterface接口并通过NativeInputManager对象向java层传送事件拦截和过滤请求。
事件监听请求在InputDispatche对象的函数notifyKey中发送,在事件输入线程接收到事件并调用InputDispatcher对象的函数notifyKey向其传送事件时,notifyKey函数根据过滤选项是否打开调用InputDispatcherPolicyInterface的接口函数filterInputEvent来发送事件过滤请求。
事件的拦截请求包括队列前的事件拦截和提交前的拦截,下面以按键事件的拦截为例进行说明。
对于队列前的按键事件拦截请求,在InputDispatche对象的notifyKey函数和injectInputEvent函数提交输入按键事件到队列之前,通过调用InputDispatcherPolicyInterface接口对象的interceptKeyBeforeQueueing函数发送事件的队列之前拦截请求;
而对于事件提交前的按键事件拦截通过调用InputDispatcherPolicyInterface接口对象的interceptKeyBeforeDispatching回调函数来发送其拦截请求,其流程如下:
在InputDispatche对象的事件提交函数dispatchKeyLocked中判断事件的policyFlags是否含有POLICY_FLAG_PASS_TO_USER,如果含有POLICY_FLAG_PASS_TO_USER就调用InputDispatche对象的postCommandLocked函数,postCommandLocked的参数为doInterceptKeyBeforeDispatchingLockedInterruptible函数指针。在postCommandLocked函数中构造一个CommandEntry对象,其command方法为参数传进来的函数指针,并放入mCommandQueue队列,然后函数dispatchKeyLocked返回,事件在事件提交之前被拦截不再继续派发。
在函数dispatchKeyLocked返回到dispatchOnce,继续调用runCommandsLockedInterruptible函数对mCommandQueue队列中的命令对象进行处理,对于mCommandQueue队列中的每一个命令对象执行命令对象的command方法,对于刚才放入的命令实际调用的是作为postCommandLocke参数的InputDispatch:doInterceptKeyBeforeDispatchingLockedInterruptible函数指针;因此doInterceptKeyBeforeDispatchingLockedInterruptible函数被调用,在doInterceptKeyBeforeDispatchingLockedInterruptible函数中调用InputDispatcherPolicyInterface接口对象的interceptKeyBeforeDispatching事件拦截回调函数发送事件提交前的拦截请求。
InputDispatcherPolicyInterface接口对象在InputDispatcher对象构造时把对NativeInputManager对象的引用作为参数传入赋值给InputDispatcher对象的InputDispatcherPolicyInterface接口类型的字段mPolicy,因此上述的对InputDispatcherPolicyInterface接口事件的调用实际调用的是NativeInputManager对象的相应函数。
而NativeInputManage对象的拦截和过滤回调函数又通过JNI调用JAVA层InputManagerService服务的相应函数,如interceptKeyBeforeDispatching、interceptKeyBeforeQueueing和filterInputEvent函数。
InputManagerService服务的事件拦截函数实际调用的是WindowManagerCallbacks接口的相应接口函数,而InputMonitor对象是WindowManagerCallbacks接口的实现,因此对WindowManagerCallbacks接口函数的调用就是对InputMonitor对象的相应函数的调用。InputMonitor对象的对应函数又通过WindowManagerService窗口管理服务中的PhoneWindowManager对象引用调用PhoneWindowManager对象的对应函数,最终完成事件的拦截处理。
而InputManagerService服务的filterInputEvent函数调用IInputFilter接口对象的相应函数进行事件过滤处理。
InputFilter对象由InputManagerService服务的setInputFilter函数赋值,并通过JNI打开InputDispatche对象的FilterEnabled标志,完成过滤后的事件又在InputFilterHost对象中采用InputManager对象的injectInputEvent函数一样的流程重新注入InputDispatch对象的mInboundQueue队列。
版权所有,转载时请尊重原创显要处注明链接,谢谢!
androd输入管理系统机制解析的更多相关文章
- 钟表维修管理系统技术解析(一) MVC架构搭建
钟表维修管理系统技术解析(一) MVC架构搭建 1.1新建项目 第一步:打开VS2010界面,点击左上角文件,点击新建,选择项目 1.1(图1) 第二步:点击网站Web类型,选择ASP.net MV ...
- 笔记:XML-解析文档-流机制解析器(SAX、StAX)
DOM 解析器完整的读入XML文档,然后将其转换成一个树型的数据结构,对于大多数应用,DOM 都运行很好,但是,如果文档很大,并且处理算法又非常简单,可以在运行时解析节点,而不必看到完整的树形结构,那 ...
- 会员卡管理系统技术解析(十八)Timer定时监听
会员卡管理系统技术解析(十八)Timer定时监听 在web应用中,有时候客户须要一些定时程序.不须要客户自己去操作.而是由应用程序自行触发(代理)运行某些操作. 这个时候监听与定时器的配合使用就基本能 ...
- 转 Java Classloader机制解析
转 Java Classloader机制解析 发表于11个月前(2014-05-09 11:36) 阅读(693) | 评论(0) 9人收藏此文章, 我要收藏 赞1 慕课网,程序员升职加薪神器,点 ...
- Java并发编程:Concurrent锁机制解析
Java并发编程:Concurrent锁机制解析 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: # ...
- 时序数据库连载系列: 时序数据库一哥InfluxDB之存储机制解析
InfluxDB 的存储机制解析 本文介绍了InfluxDB对于时序数据的存储/索引的设计.由于InfluxDB的集群版已在0.12版就不再开源,因此如无特殊说明,本文的介绍对象都是指 InfluxD ...
- CDN 的缓存与回源机制解析
CDN的缓存与回源机制解析 CDN (Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器.这些服务器存储着数据的副本,因此服务器可以根据哪些服务器与用户 ...
- 16.Spark Streaming源码解读之数据清理机制解析
原创文章,转载请注明:转载自 听风居士博客(http://www.cnblogs.com/zhouyf/) 本期内容: 一.Spark Streaming 数据清理总览 二.Spark Streami ...
- 【转载】linux2.6内核initrd机制解析
题记 很久之前就分析过这部分内容,但是那个时候不够深入,姑且知道这么个东西存在,到底怎么用,来龙去脉咋回事就不知道了.前段时间工作上遇到了一个initrd的问题,没办法只能再去研究研究,还好,有点眉目 ...
随机推荐
- ROS机器人程序设计(原书第2版)学习镜像分享及使用说明
ROS机器人程序设计(原书第2版)学习镜像分享及使用说明 系统用于ROS爱好者学习交流,也可用于其他用途,并不局限于ROS. 这款镜像文件是基于一年前的Ubuntu ROS Arduino Gazeb ...
- ROS机器人程序设计(原书第2版)补充资料 (捌) 第八章 导航功能包集入门 navigation
ROS机器人程序设计(原书第2版)补充资料 (捌) 第八章 导航功能包集入门 navigation 书中,大部分出现hydro的地方,直接替换为indigo或jade或kinetic,即可在对应版本中 ...
- Python 2.7的字典实现简化版(C语言)
这是一个能自动调整大小的哈希字典,外部接口实现了下列功能. 1.字典级别: 创建字典 dict_new 归零字典 dict_clear 2.键值级别: 查找 dict_search 强制查找 dict ...
- Android必知必会-App 常用图标尺寸规范汇总
若移动端访问不佳,请使用 –> Github版 内容持续更新中,更新日期:2016-08-11 1. 程序启动图标(icon launcher) 放在mipmap-*dpi下,文件名为ic_la ...
- Bootstarp-table入门
介绍 介绍什么的,大家自己去下面的网站看 Bootstrap中文网:http://www.bootcss.com/ Bootstrap Table Demo:http://issues. ...
- lucene查询索引库、分页、过滤、排序、高亮
2.查询索引库 插入测试数据 xx.xx. index. ArticleIndex @Test public void testCreateIndexBatch() throws Exception{ ...
- Shell脚本生成网页版相册浏览器
今天学到了一招,那就是使用脚本制作一款网页版相册浏览器.先上图吧. 必备基础 操作系统: 以linux为内核的操作系统都行 编程语言:Shell(bash)脚本,相关基础知识即可 下载工具:wget ...
- 关于ROS学习的一些反思
距离发布上一篇ROS的博客已经过去两年了,才发现原来自己已经这么久可没有写过关于ROS的文章,想来很是惭愧.这两年时间,自己怀着程序员的梦想,研究过RTOS,探索过Linux,编写过Android应用 ...
- Dynamics CRM2016 Supported versions of Internet Explorer and Microsoft Edge
在CRM2016发布在即之时,让咱们看下新版的CRM对IE及Edge的支持 这次和以往不同,官方给出的不只是IE几以上支持,IE几以下不支持,而是有一个对应的系统列表,具体看下表. 当然你也可以说我I ...
- Python pygame安装过程笔记
今天看到一个教程,是关于Python安装pygame模块的.觉得很好,拿来分享一下. 安装Python 额,这个小题貌似在这里很是多余啊.但是为了照顾到刚刚学习Python的童鞋,我还是多啰嗦两句吧. ...