欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

理解输入系统和IMS

程序员文章站 2022-06-17 10:20:05
一.输入事件传递流程的组成部分需要输入系统和Android系统的其他成员来共同实现输入事件的传递,如下所示:输入事件流程分为三部分:输入系统部分、WMS处理部分、View处理部分1.输入系统输入系统主要分为输入子系统和InputManagerService(IMS)。输入事件产生的原始信息会被Linux内核中的输入子系统采集,原始信息由Kernel space的驱动层一直传递到User space的设备节点。Android提供了getevent和sendevent两个工具帮助开....

一.输入事件传递流程的组成部分

需要输入系统和Android系统的其他成员来共同实现输入事件的传递,如下所示:

理解输入系统和IMS

输入事件流程分为三部分:输入系统部分、WMS处理部分、View处理部分

1.输入系统 

输入系统主要分为输入子系统和InputManagerService(IMS)。

输入事件产生的原始信息会被Linux内核中的输入子系统采集,原始信息由Kernel space的驱动层一直传递到User space的设备节点。

Android提供了getevent和sendevent两个工具帮助开发者从设备节点读取输入事件和写入输入事件。

输入系统部分如下图:

理解输入系统和IMS

 IMS所做的工作就是监听/dev/input下的所有设备节点,当设备节点有数据时会将数据进行加工处理并找到合适的窗口,将输入事件派发给它。

2.WMS处理部分

WMS的职责之一就是输入系统的中转站,WMS作为窗口的管理者,会配合IMS将输入事件交由合适的窗口来处理。

3.View处理部分

输入事件一般情况下最终会交由View来处理。

 

二.IMS的诞生

1.SystemServer处理部分

在其他服务中先创建IMS,然后创建WMS,需要注意的是创建WMS过程中有个参数就是IMS,WMS是输入系统的中转站,其内部包含了IMS的引用。最后WMS和IMS添加到ServiceManager中统一管理。

2.InputManagerService构造方法

InputManagerService的构造方法--------->InputManagerService.cpp层的nativeInit------>NativeInputManager的构造方法------->InputManager的构造方法------>创建InputReader和InputDispatcher

理解输入系统和IMS

InputManagerService构造方法: 创建android.display线程的Looper的InputManagerHandler,android.display线程是系统共享的单例前台线程,这个线程内部执行了WMS的创建。然后调用nativeInit方法是通过jni调用到c层。

nativeInit:调用NativeInputManager的构造方法。

NativeInputManager构造方法:创建EventHub和InputManager,EventHub通过Linux内核的INotify与Epoll机制监听设备节点,通过EventHub的getEvent函数读取设备节点的增删事件和原始输入事件。

InputReader:会不断循环读取EventHub中的原始输入事件,将这些原始输入事件交由InputDispatcher处理,InputDispatcher保存了WMS的所有窗口信息,InputDispatcher可以将输入事件派发给合适的窗口。InputReader和InputDispatcher都是耗时操作,因此在initialize函数中创建InputReaderThread和InputDispatcherThread线程供他们使用。

3.IMS的启动过程

SystemServer的startOtherServices方法------->inputManager的start方法------->InputManagerService.cpp的nativeStart方法------>启动InputReaderThread和InputDispatcherThread线程

inputManager的start方法:将自身添加到Watchdog中进行监控。动态注册对应nativeStart方法。

4.InputDispatcher的启动过程

InputManager.cpp的构造方法------->创建InputDispatcher和InputReader-------->threadLoop------>dispatchOnce

创建InputDispatcher:InputDispatcherThread继承Thread。native的Thread内部有一个循环,当线程运行时,会调用threadLoop函数。

dispatchOnce:用来检查InputDispatcher的缓存队列中是否有等待处理的命令,如果没有就执行dispatchOnceInnerLocked函数它用来将输入事件分发给合适的窗口。最后调用Looper的pollOnce函数使InputDispatcherThread进入睡眠状态。当有输入事件产生时,InputReader就会将睡眠状态的InputDispatcher唤醒,InputDispatcher会重新开始分发事件。

5.InputReader处理事件的过程

InputReaderThread的threadLoop-------->InputReader的loopOnce------>processEventsLocked------>processEventsForDeviceLocked------>KeyboardInputMapper的process------>processKey------>InputDispatcher的notifyKey

InputReaderThread的threadLoop:InputReader是在InputReaderThread中启动的,InputReaderThread和InputDispatcherThread的定义也是类似的,也继承了Thread并定义了threadLoop纯虚函数。

loopOnce:调用getEvent函数来获取设备节点的事件信息。事件信息主要有两种:一种是设备节点的增删事件(设备事件),另一种是原始输入事件,processEventsLocked函数用于对原始输入信息进行加工处理,加工处理后的输入事件交由InputDispatcher来处理。

processEventsLocked:遍历所有事件,将原始事件和设备事件分开处理,同一个设备的输入事件交由processEventsForDeviceLocked函数来处理。

KeyboardInputMapper的process:真正加工原始输入事件的是InputMapper对象,InputMapper有很多子类用于加工不同的原始输入事件,比如:KeyboardInputMapper用于处理键盘输入事件,TouchInputMapper用于处理触摸事件。

processKey:将事件封装成NotifyKeyArgs通知给InputDispatcher,交由InputDispatcher处理。

notifyKey:是否需要唤醒InputDispatcherThread线程,然后对事件输入进行分发。

6.输入事件的处理总结

分别涉及四个关键的类:IMS、EventHub、InputDispatcher和InputReader

1.IMS启动了InputDispatcherThread和InputReaderThread,他们分别用来运行InputDispatcher和InputReader.

2.InputDispatcher先于InputReader创建,InputDispatcher的dispatchOnceInnerLocked函数用来将事件分发给合适的窗口。InputDispatcher没有输入事件处理时会进入休眠状态,等待InputReader来唤醒。

3.InputReader通过EventHub的getEvent函数来获取事件信息,如果是原始输入事件,就将这些原始输入事件交由不同的InputMapper来处理,最终交由InputDispatcher来处理。

4.InputDispatcher的notifyKey函数中会根据按键数据来判断InputDispatcher是否要被唤醒,InputDispatcher被唤醒后,会重新调用dispatchOnceInnerLocked函数将输入事件分发给合适窗口。 

理解输入系统和IMS

7.InputReader的加工类型

InputDispatcher的notifyKey方法用于唤醒InputDispatcherThread,它的参数是NotifyKeyArgs是InputReader对按键类型事件加工后得到的。

NotifyKeyArgs结构体继承自NotifyArgs结构,NotifyArgs有三个子类,分别是NotifyKeyArgs、NotifyMotionArgs、NotifySwitchArgs。

InputReader对原始输入事件进行加工后,最终得出三种事件类型,分别是key事件,motion事件,swtich事件。将这些事件交由InputDispatcher进行分发。

8.InputDispatcher的分发过程

以下是对Motion事件分发的过程。

InputDispatcher的notifyMotion唤醒InputDispatcherThread------->执行InputDispatcherThread的threadLoop------>执行InputDispatcher的dispatchOnce------->dispatchOnceInnerLocked

dispatchOnce:设置休眠时间,设置是否休眠,调用dispatchOnceInnerLocked进行时间分发

dispatchOnceInnerLocked:如果InputDispatcher是冻结状态则不进行分发,进行窗口切换操作,当InputDispatcher处理分发完事件后,第一时间来处理窗口切换操作,取出事件,如果没有待分发事件,则再队列中取出一个事件,事件丢弃,根据事件丢弃原因进行区分处理,后续处理,进行内存释放,为InputDispatcher能够快速处理下一个事件进行准备。

9.事件分发到目标窗口的过程

InputDispatcher的dispatchOnceInnerLocked函数------->dispatchMotionLocked------->dispatchEventLocked------->prepareDispatcherCycleLocked------->inputTarget的inputChannel和窗口进行进程间通讯,发送给目标窗口

dispatchOnceInnerLocked:事件丢弃和调用dispatchMotionLocked函数为Motion事件寻找合适窗口。

dispatchMotionLocked:如果事件需要丢弃则直接返回true,不会为该事件寻找窗口,这次分发任务就没有完成,会在下一次InputDispatcherThread的循环中再次尝试分发。对触摸事件findTouchedWindowTargetsLocked处理和非触摸事件分别处理。将事件分发到inputTargets列表中。inputTargets里面存储的是结构体,最终将事件分发给inputTargets列表中的目标。

findTouchedWindowTargetsLocked:先获取MotionEntry中坐标点,以便后面筛选窗口使用,获取mWindowHandles的InputWindowHandle数量,InputWindowHandle中保存了InputWindowInfo,InputWindowInfo中又包含了WindowManager.LayoutParams定义的窗口标志,除了窗口标志,InputWindowInfo中还包含InputChannel和窗口的各种属性,InputWindowInfo描述了可以接收输入事件的窗口属性。这样看来,InputWindowHandle和WMS中的WindowState很相似。通俗的说,WindowState用来代表WMS中的窗口,而InputWindowHandle用来代表系统中的窗口。

遍历mWindowHandles列表中的窗口,找到触摸过的窗口和窗口之外的外部目标。如果窗口是focusable或者flag不为FLAG_NOT_FOCUSABLE,则说明窗口是可触摸的,如果窗口是可触摸的或者坐标点在窗口之上,则会将windowHandle赋值给newTouchedWindowHandle。最后会将windowHandle赋值给newTouchedWindowHandle,并添加到TempTouchState中。

 

本文地址:https://blog.csdn.net/gongjdde/article/details/110357620

相关标签: Android Framework