【转】【Android】事件输入系统-代码层次解读

  本文基于Android-4.0 原文:http://www.cnblogs.com/lcw/p/3374466.htmlhtml

理论层次解读

  请看:www.cnblogs.com/lcw/p/3373214.htmlnode

如何管理各类驱动设备

  在理论中谈到EventHub,这个一看就是一个作实事的,确定不是领导,哪它的领导是谁呢?android

  从如下几方面来分析此问题:数据结构

  1. 每一个功能模块是怎么产生的?
  2. 读取设备输入流程?
  3. 事件分发流程?

各个功能模块是怎么产生的?

  先看一下每一个模块的工做职责:EventHub, InputReader, InputManager...app

模块功能

EventHub

  它是系统中全部事件的中央处理站。它管理全部系统中能够识别的输入设备的输入事件,此外,当设备增长或删除时,EventHub将产生相应的输入事件给系统。异步

  EventHub经过getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。并且EventHub还跟踪每一个输入调入的能力,好比输入设备的类别,输入设备支持哪些按键。 函数

InputReader

  InputReader从EventHub中读取原始事件数据(RawEvent),并由各个InputMapper处理以后输入对应的input listener.oop

  InputReader拥有一个InputMapper集合。它作的大部分工做在InputReader线程中完成,可是InputReader能够接受任意线程的查询。为了可管理性,InputReader使用一个简单的Mutex来保护它的状态。this

  InputReader拥有一个EventHub对象,但这个对象不是它建立的,而是在建立InputReader时做为参数传入的。spa

InputDispatcher

  InputDispatcher负责把事件分发给输入目标,其中的一些功能(如识别输入目标)由独立的policy对象控制。

InputManager

  InputManager是系统事件处理的核心,它虽然不作具体的事,但管理工做仍是要作的,好比接受咱们客户的投诉和索赔要求,或者老板的出所筒。

     InputManager使用两个线程:

  1. InputReaderThread叫作"InputReader"线程,它负责读取并预处理RawEvent,applies policy而且把消息送入DispatcherThead管理的队列中。
  2. InputDispatcherThread叫作"InputDispatcher"线程,它在队列上等待新的输入事件,而且异步地把这些事件分发给应用程序。

  InputReaderThread类与InputDispatcherThread类不共享内部状态,全部的通讯都是单向的,从InputReaderThread到InputDispatcherThread。两个类能够经过InputDispatchPolicy进行交互。

  InputManager类从不与Java交互,而InputDispatchPolicy负责执行全部与系统的外部交互,包括调用DVM业务。

 


建立流程

  1. 在android_server_InputManager_nativeInit中建立NativeInputManager对象,并保存到gNativeInputManager中;
  2. 在建立NativeInputManager对象时,它会建立EventHub对象<且建立是其成员mNeedToScanDevices的值为true>,而后把刚建立的EventHub对象做为参数建立InputManager对象;
  3. 在建立InputManager对象时,建立InputReader对象,而后把它做为参数建立InputReaderThread;建立InputDispatcher对象,而后把它做为参数建立InputDispatcherThread对象;注意:以上两个线程对象都有本身的threadLoop函数,它将在Thread::_threadLoop中被调用,这个Thread::_threadLoop是线程入口函数,线程在Thread::run中被真正地建立)

建立InputReader对象

  1. 把EventHub、readerPolicy<实质为NativeInputManager对象>和建立的InputDispatcher对象做为参数建立InputReader对象:mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
  2. 在建立InputReader时, 保存EventHub对象到mEventHub中,并建立QueuedInputListener对象并保存在mQueuedListener中

建立InputDispatcher对象

  1. 把传入的参数dispatcherPolicy<实质为NativeInputManager对象>做为参数建立InputDispatcher对象:mDispatcher = new InputDispatcher(dispatcherPolicy);
  2. 在建立InputDispatcher时,建立了一个looper对象:mLooper = new Looper(false);

 


启动流程

  1. 在android_server_InputManager_nativeStart中调用InputManager::start,代码以下:
    result = gNativeInputManager->getInputManager()->start();

     

  2. 在InputManager::start中,调用mDispatcherThread->run和mReaderThread->run,代码以下:
    result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

  在上面的Thread::run中,调用createThreadEtc函数,并以Thread::_threadLoop做为入口函数,以上面的mDispatcherThread或mReaderThread做为userdata建立线程。

  至此InputReader线程和InputDispatcher线程都已经工做,详细信息见Thread::_threadLoop,在此函数中它将调用mDispatcherThread或mReaderThread的threadLoop函数来作真正的事。

mReaderThread->threadLoop

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

mDispatcherThread->threadLoop

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
} 

EventInput对象关系图 

  

 


设备操做流程

  从EventHub::getEvents读取的事件数据结构以下:

  View Code

  读取事件时的调用流程为:

Thread::_threadLoop->

     InputReaderThread::threadLoop->

          InputReader::loopOnce->

               EventHub::getEvents->

打开设备

  在EventHub::getEvents中,当mNeedToScanDevices为true时<当建立EventHub对象时,它就为true>,它将从/dev/input目录下查找全部设备,并进行打开,获取其相关属性,最后加入mDevices列表中。

EventHub::scanDevicesLocked->

     EventHub::scanDirLocked("/dev/input")->

         EventHub::openDeviceLocked

打开事件输入设备

  打开事件输入设备,在用户态调用open,则在kernel态中调用evdev_open函数,evdev_open处理流程以下:

  1. 首先从参数inode中获取在evdev_table中的索引,从而获取对应的evdev对象
  2. 建立evdev_client对象,建立此对象时同时为其buffer成员分配对应的内存
  3. 把新建立evdev_client对象添加到client_list链表中
  4. 把client保存在file的private_data中
  5. 调用evdev_open_device->input_open_device->input_dev.open函数打开设备。

 


读取输入事件

  要说EventHub::getEvents如何获取输入事件,不得不先说说它的几个相关的成员变量:

  1. mPendingEventCount:调用epoll_wait时的返回值,若是没有事件,则其值为0;
  2. mPendingEventIndex:当前须要处理的事件索引
  3. mEpollFd:epoll实例,在EventHub::EventHub中初始化此例,全部输入事件经过epoll_wait来获取,每个事件的数据结构为:struct epoll_event

  :epoll_event只代表某个设备上有事件,并不包含事件内容,具体事件内容须要经过read来读取

查看设备上是否有事件

  在调用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)以后,读到的epoll_event事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount,固然,在调用epoll_event以前,mPendingEventIndex被清0,直正的事件处理在下面的代码中。

  View Code

读取设备上真正的事件

  epoll_wait只是告诉咱们Device已经有事件了,让咱们去读,真正读取设备输入事件的代码如上,其流程以下:

  1. 根据eventItem.data.u32获取设备索引,从而获取对应的Device
  2. 从device->fd中读取input_event事件。

  read(device->fd, readBuffer, sizeof(struct input_event) * capacity);这些input_event是由各个注册的input_device报告给input子系统的。

  至此,事件已经读取到用户态,如今看看EventHub怎么处理这些事件?

 


处理输入事件

  首先经过epoll_wait查看哪些设备有事件,而后经过read从有事件的设备中读取事件,如今事件已经读取到用户态,且数据结构为input_event,保存在EventHub::getEvents的readBuffer中。

  下面就看看这些事件下一步的东家是谁?

      1)首先把input_event的信息填入RawEvent中,其相关代码以下:

  View Code

  2)若是是input_event的类型为EV_KEY,则须要调用device->keyMap.keyLayoutMap->mapKey函数把iput_event.code映射为RawEvent.keyCode。

  相关数据结构关系以下图所示:

  

  至此,EventHub::getEvents读取事件的任务已经完成。

  下面看看RawEvent的命运如何呢?

InputReader::loopOnce如何处理RawEvent?

  先温习一下读取事件时的调用流程为:

Thread::_threadLoop->

     InputReaderThread::threadLoop->

          InputReader::loopOnce->

               EventHub::getEvents->

  在InputReader::loopOnce中,当调用EventHub->getEvents获取到RawEvent以后,调用InputReader::processEventsLocked来处理这些事件,而后调用mQueuedListener->flush()把这些队列中的事件发送到Listener。

InputReader::processEventsLocked

  在InputReader::processEventsLocked主要分两步处理:

  1. 处理来自于事件驱动设备的事件(processEventsForDeviceLocked)
  2. 处理设备增长、删除和修改事件

  增长事件的流程为:为一个新增的Device建立一个InputDevice,并增长到InputReader::mDevices中;根据新增长设备的class类别,建立对应的消息转换器(InputMapper),而后此消息转换器加入InputDevice::mMappers中。消息转换器负责把读取的RawEvent转换成特定的事件,以供应用程序使用。

  InputMapper关系以下图所示

  

EventHub与InputReader各自管理功能
  1. EventHub管理一堆Device,每个Device与Kernel中一个事件输入设备对应
  2. InputReader管理一堆InputDevice,每个InputDevice与EventHub中的Device对应
  3. InputDevice管理一些与之相关的InputMapper,每个InputMapper与一个特定的应用事件相对应,如:SingleTouchInputMapper。
InputReader::mQueuedListener->flush()

  

  processEventsLocked已经把来自于事件设备的事件煮熟以后放入到各类NotifyArgs(如NotifyMotionArgs)之中,而后把这些各类NotifyArgs加入InputReader::mQueuedListener::mArgsQueue链表中。本Flush函数就是要把mArgsQueue中的全部NotifyArgs进行处理。

  View Code

 

  调用链表中每一个NotifyArgs的notify函数,且有一个有意思的参数 mInnerListener,这个参数在前面屡次提到过,它是在建立mQueuedListener时提供的,它其实就是InputManager中的mDispatcher,前面一直在InputReader中打转转,如今终于看到InputDispatcher登场了,说明事件很快就能够谢幕了。

  再向下看一下,这么多类NotifyArgs,为描述方便,以NotifyMotionArgs为例,其代码为: 

void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {  
    listener->notifyMotion(this);  
}  

  下面就看看InputDispatcher(mDispatcher)的notifyMotion函数作了些什么。

  这个InputDispatcher::notifyMotion(const NotifyMotionArgs* args)可就不简单了。

  在InputDispatcher::notifyMotion中:

  1. 根据NotifyMotionArgs提供的信息,构造一个MotionEvent,再调用mPolicy->filterInputEvent看是否须要丢弃此事件,若是须要丢弃则立刻返加。其中mPolicy为NativeInputManager实例,在构造InputDispatcher时提供的参数。
  2. 对于AMOTION_EVENT_ACTION_UP或AMOTION_EVENT_ACTION_DOWN事件,则直接根据NotifyMotionArgs提供的信息,构造一个MotionEntry。
  3. 调用InputDispatcher::enqueueInboundEventLocked把新构造的MotionEntry添加到InputDispatcher::mInboundQueue,并返回是否须要唤醒mLooper<向pipe中写入数据>的标识。

  以上操做都是在InputReader线程中完成的,如今应该InputDispatcher线程开始工做了。

 


分发输入事件

  InputDispatcherThread主循环以下:

Thread::_threadLoop->

   InputDispatcherThread::threadLoop->

      mDispatcher->dispatchOnce(InputDispatcher::dispatchOnce)->

          dispatchOnceInnerLocked then

          mLooper->pollOnce

  先看看简单的mLooper->pollOnce

mLooper->pollOnce 

  其功能为等待超时或被pipe唤醒(InputReader线程调用InputDispatcher::notifyMotion时, InputDispatcher::notifyMotion根据状况调用mLooper->wake)。

  其调用流程以下:

mLooper->pollOnce(int timeoutMillis)->

  Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)->

dispatchOnceInnerLocked  

  1. 从mInboundQueue从中依次取出EventEntry<MotionEntry的基类>,
  2. 调用InputDispatcher::dispatchMotionLocked处理此MotionEntry
  3. 调用InputDispatcher::dispatchEventToCurrentInputTargetsLocked

  对于InputDispatcher::mCurrentInputTargets中的每个InputTarget,并获取对应的Connection,调用InputDispatcher::prepareDispatchCycleLocked,

  其相关代码以下:

  View Code

InputDispatcher::prepareDispatchCycleLocked

  1. 调用enqueueDispatchEntryLocked建立DispatchEntry对象,并把它增长到Connection::outboundQueue队列中。
  2. 调用activateConnectionLocked把当前Connection增长到InputDispatcher::mActiveConnections链表中
  3. 调用InputDispatcher::startDispatchCycleLocked,接着它调用Connection::inputPublisher.publishMotionEvent来发布事件到ashmem buffer中调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它有一个新的消息到了,快来消费吧!

  至此,整个Android事件处理系统就介绍完了。

 


分析总结

  下面是我作"电视棒远程万能遥控器"项目时写的分析,只列出了关键步骤

  View Code
相关文章
相关标签/搜索