【轉】【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
相關文章
相關標籤/搜索