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输入管理系统机制解析,布布扣,bubuko.com

时间: 2024-10-10 16:44:35

androd输入管理系统机制解析的相关文章

笔记:XML-解析文档-流机制解析器(SAX、StAX)

DOM 解析器完整的读入XML文档,然后将其转换成一个树型的数据结构,对于大多数应用,DOM 都运行很好,但是,如果文档很大,并且处理算法又非常简单,可以在运行时解析节点,而不必看到完整的树形结构,那么我们应该使用流机制解析器(streaming parser),Java 类库提供的流解析机制有 SAX 解析器和 StAX 解析器,SAX 解析器是基于事件回调机制,而 StAX解析器提供了解析事件的迭代器. 使用SAX解析器 SAX 解析器在解析XML 输入的组成部分时会报告事件,在使用 SAX

Chromium网页输入事件处理机制简要介绍和学习计划

用户在浏览网页的时候,需要与网页进行交互,常用的操作如滑动.捏合网页,以及点击网页中的链接等.这些交互操作也称为用户输入事件,浏览器需要对它们作出迅速的响应,例如及时更新网页内容或者打开新的网页等.浏览器能够对用户输入事件作出迅速的响应是至关重要的,因为这关乎到用户浏览网页时的体验,尤其是在用户滑动和捏合网页时.本文接下来就简要介绍Chromium对用户输入事件的处理机制,以及制定后续的学习计划. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 在任何一个

钟表维修管理系统技术解析(一) MVC架构搭建

钟表维修管理系统技术解析(一)  MVC架构搭建 1.1新建项目 第一步:打开VS2010界面,点击左上角文件,点击新建,选择项目 1.1(图1) 第二步:点击网站Web类型,选择ASP.net MVC3 Web应用程序,在名称中输入项目名称(解决方案名称),位置可自行选择存放该项目的路径,完成后点击确定 1.1(图2) 第三步:选择空项目,点击确定,然后MVC项目创建成功 1.1(图3) 1.1(图4) 1.2创建控制器和视图 第一步:右键点击Controllers(控制器),点击添加,选择控

Java并发编程:Concurrent锁机制解析

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

会员卡管理系统技术解析(十八)Timer定时监听

会员卡管理系统技术解析(十八)Timer定时监听 在web应用中,有时候客户需要一些定时程序,不需要客户自己去操作,而是由应用程序自行触发(代理)执行某些操作.这个时候监听与定时器的配合使用就基本可以实现这个需求了.网上很多代码并不完整,水平未到,看得太吃力了.下面以系统每天00:00:00进行的当日最大单据数维护进行实例解析. 首先,建立相关的包河类,如下图: 图1 然后,在"web.xml"中对定时器和监听类注册,代码如下: <servlet> <servlet-

android permission权限与安全机制解析(下)

在android permission权限与安全机制解析(上)篇博客中,我已经详细介绍了android相关系统permission和自定义permission,以及一些权限机制和安全机制.这篇博客主要将会介绍到android 6.0的相关权限更改,原理和相关的处理方式,解决方法等. 就以我以前的一个仿最新版微信相册为例子来分析. android 6.0权限全面详细分析和解决方案 Marshmallow版本权限修改 android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次

转 Java Classloader机制解析

转 Java Classloader机制解析 发表于11个月前(2014-05-09 11:36)   阅读(693) | 评论(0) 9人收藏此文章, 我要收藏 赞1 慕课网,程序员升职加薪神器,点击免费学习 目录[-] JDK默认ClassLoader 双亲委托模型 如何自定义ClassLoader 1.loadClass 方法 2.findClass 3.defineClass 不遵循“双亲委托机制”的场景 做Java开发,对于ClassLoader的机制是必须要熟悉的基础知识,本文针对J

java classLoader机制解析

类从被加载到虚拟机内存中开始,到卸装出内存为止,它的整个生命周期包括了:加载,连接(验证,准备,解析),初始化,使用和卸载七个阶段.其中验证.准备和解析三个部分称为连接,也就是说,一个Java类从字节代码到能够在JVM中被使用,需要经过加载.链接和初始化这三个步骤 .我们看一看Java虚拟机的体系结构. Java虚拟机的体系结构如下图所示: Java类加载的全过程,是加载.验证.准备.解析和初始化这五个阶段的过程.而加载阶段是类加载过程的一个阶段.在加载阶段,虚拟机需要完成以下三件事情: 通过一

Sql Server Tempdb原理-日志机制解析实践

笔者曾经在面试DBA时的一句”tempdb为什么比其他数据库快?”使得95%以上的应试者都一脸茫然.Tempdb作为Sqlserver的重要特征,一直以来大家对它可能即熟悉又陌生.熟悉是我们时时刻刻都在用,陌生可能是很少有人关注它的运行机制.这次我将通过实例给大家介绍下tempdb的日志机制. 测试用例 我们分别在用户数据库(testpage),tempdb中创建相似对象t1,#t1,并在tempdb中创建创建非临时表,然后执行相应的insert脚本(用以产生日志),并记录执行时间用以比较用以比