这一期我们来看一下InputReader的启动过程,以及他在处理消息的时候所做的一些工作,以及如何把消息传给inputdispatcher这个对象,使这个对象把消息发送出去。
首先我们看一下InputReader这个对象,他在初始化的时候会传进来两个参数,一个是InputDevice,也就是EventHub,另一个就是listener,这个listener就是我们的inputDispatcher,然后还有一个process,他在初始化的时候会根据我们input的类型,来创建几个相应的mapper,InputMapper主要包括上边绿色边框的四个类型。我们每一个事件得到之后,都会调用相应的mapper中的方法,来对键值进行处理。下面我们在代码下看一下InputReader的初始化过程,我们打开framework/base/services/input/InputManager.cpp。我们来看一下InputManager初始化的过程
首先他在这里会创建一个InputDispatcher,在后面还会创建一个mReader的一个对象,这个对象就是InputReader类的对象,这个对象在实例化的时候传了三个参数,一个是EventHub,就是我们上期所讲的,和我们底层设备所对接的那一层;还有一个是readerPolicy,这个是我们JNI层的一个对象;第三个就是我们的mDispatcher,这个是我们分发按键的对象。我们来看一下InputReader初始化的时候所做的事情
初始化的时候在这里创建了一个QueuedInputListerer对象,他会把listener当做一个参数,这个对象就是监听我们input读到消息之后把消息通过queued的listener,把这个消息告诉我们的Dispatcher。再往下会有一些相应的设置,我们现将他们忽略。我们来看一下他是如何获取消息的,我们看一下getEvents
上层每调用一次消息,那么我们在这里就会调用我们的EventHub->getEvents来获取消息。再往下我们看一下这个下消息的类型,如果说我们这里有inputDevicesChanged,那么他会告诉我们上层这个设备有改变。比如我们的热插拔设备,添加或者减少一个设备,那么他这里就会告诉上层,而我们InputReader正真处理消息是通过processEventsLocked这个方法来处理消息的。
这个消息是当我们调用EventHub的到消息的时候,传的其实就是RawEvent的一个对象,这个对象传上来之后就会给他赋值。我们来看一下处理的过程。首先我们要处理的就是我们的DEVICE_ADDED,我们第一次上来之后,EventHub因为是去扫描我们的设备,然后把这些设备添加到我们的对象中。
我们来看一下添加设备的过程,首先他会的到我们对象的一些属性,包括他的类的一个属性,然后会创建一个InputDevice。我们来看一下这个创建的过程
在这里他会创建mapper,也就是根据设备的一些属性来创建这个mapper。如果说我们是INPUT_DEVICE_CLASS_SWITCH,那么在这里会创建一个SwitchInputMapper,如果是虚拟的,会创建一个虚拟的mapper。再往下会有一个KeyboardInputMapper,包括CursorInputMapper。我们来看一下KeyboardInputMapper的创建过程,我们找一下process函数
如果有一个键值上来之后,我们在这里会使用这个来处理我们的键值。在这里它会使用getEventHub的mapKey,来对我们的key进行一下处理。然后调用我们的processKey消息。在这个消息中会对按键做一个操作,将这个按键值到时候会发送给我们其他的设备,这就是我们process所做的工作。
如果我们把键值拿到之后,他会在这里调一个getListener的一个notifyKey来通知我们的listener,而我们的listener就是我们的inputdispatcher,刚才在我们InputReader初始化的时候,就给我们的listener已经设置好了。在这里创建NotifyKeyArgs,将这个args作为一个参数传给我们的InputDispatcher,这个是我们处理案件的消息,同样的他还有一个EV_MSC的消息和EV_SYN的消息。下面我们来看一下触摸的一个事件,触摸事件也有一个自己的process处理函数,触摸事件分为单点触摸和多点触摸。下面我们来看一下多点触摸的处理过程
他会把X Y 值以及一些相关的属性全部做成一个键值,发送给我们的Dispatcher,最后他会调用一个finishSync,把这个消息传给我们上层的应用,这个就是触摸屏所做的一个事情。后边还包括游戏手柄之类的,全部是这种方式来处理的。通过建立这一系列的mapper之后呢,我们在这里获取消息之后,他就会在这里走一个mapper的处理过程。如果是DEVICE_REMOVED我们就把这个设备删掉。我们来看一下InputReader是如何来处理正常的键值消息的,他走的是processEventsForDeviceLocked这个方法,我们来看一下
他首先会根据我们device的ID,拿到我们的设备然后使用InputDevice来处理我们的键值,我们这个处理过程就是刚才所说的那个mapper。比如你的一个设备,和我的列表中的一致,那么我们就找到你相应的位置,如果你是key的那么我们就找key的mapper来做处理,如果是其他的,就找其他的来做处理,这个处理的过程我们刚才月分析了一个keyeventsmapper的一个处理方式。他就是通过listener的notify的方法,把这个消息通知给我们的InputDispatcher,而它就会把这个消息做一下处理发送给我们上层的WindowManagerService,这就是InputReader他所做的事情。
下面我们再来看一下InputDispatcher的notifyKey,他处理的过程是对我们键值进行一系列的标志性操作,做一些初始化的工作,后面会把我们的事件添加到消息队列中。他在这里创建了一个newEntry的对象,在这里又调用了一个enqueueInboundEventLocked,把这个事件添加到我们的一个队列中,我们来看一下这个方法
在这里首先会调用mInboundQueue的enqueueAtTail的方法,把我们entry这个事件添加到我们这个队列中,然后在这里面根据事件的类型来做一些处理,然后看一下是否要唤醒。当我们把这个事件添加到队列中后,我们来看一下真正Dispatcher的位置是在哪里,我们来看一下Dispatcher的loop,其实调用的是我们的dispatcherOnce的方法
在这里调用的就是我们的dispatcherOnceInnerLocked方法去做真正的向上层传递键值。在这个dispatcherOnceInnerLocked函数中,他会判断一下mInboundQueue是否为空,如果不是空,那么后面我们会把这个消息发出去。我们找到dispatchEvebtLocked真正发送的位置,他首先会lock住然后这里会有一个connection,而connect就是我们上层做注册的时候所得到的inputchannel,这个inputchannel是和我们上层的WindowManagerService来连接起来的。当我们拿到这个connect之后。我们就会调用prepareDispatchCycleLocked去发送消息。
我们再来看一下整个的数据流程
首先EventHub要将我们底层的设备全部打开,然后对我们InputReader提供了一个getevent的一个接口。在InputReader里面有一个mDevices,他会根据我们EventHub的设备的类型mapper出来一系列的操作对象,而每一个操作对象都会对我们的键值和input事件做一个处理,我们处理的过程还可以看到我们的InputReader还有一个listener,这个listener就是我们的dispatch。当我们收到一条消息之后,我们来看一下这条黄线,如果有一个key消息,那么会使用keyboard这个mapper来处理我们这个消息。Process处理完之后他会通过listener的方式通知InputDispatcher,而他收到这个消息之后,他会添加到我们本地的一个队列中,添加完之后他告诉我们的InputDispatcher来执行一下looponce,之后把消息发送给我们的上层的java部分,也就是WindowManagerService,这样我们的整个数据流程就清晰了。