1. 程式人生 > >Android Framework---之Input子系統

Android Framework---之Input子系統

轉自:https://www.cnblogs.com/haiming/p/3318614.html 

參考:

https://blog.csdn.net/jinzhuojun/article/details/41909159 //推薦,native層keyevent的傳遞

https://blog.csdn.net/u010164190/article/details/52964829

https://blog.csdn.net/chenweiaiyanyan/article/details/72866796

https://blog.csdn.net/jun5753/article/details/78684049 //推薦,java層keyevent的傳遞

https://blog.csdn.net/fe421504975/article/details/8272102

https://blog.csdn.net/myarrow/article/details/7091061

http://blog.sina.com.cn/s/blog_89f592f50101394l.html

https://blog.csdn.net/fe421504975/article/details/8272102

 

下面這是基於Android4.2程式碼的關於Input子系統的筆記。在這篇筆記中,只涉及Android相關的東西,關於Linux核心中對各種輸入裝置的統一,在本文中不作說明。此外,由於才疏學淺,文中難免有錯誤的地方,希望各位路過的大神能夠予以指出。閒話少敘,先看一張我自己設計的圖,如下:

這幅圖是為了便於個人理解畫出的,裡面的註釋也比較明白,就不再說明。本文就是以這幅圖為基本的思路,簡述在Android4.2系統中和Input子系統的相關一些內容。如圖,本文將分為以下幾個部分敘述:

(0)Input系統的啟動

(1)InputReader的功能,以及執行的流程

(2)InputDispatcher的功能,及執行流程

(3)Input子系統中的通訊方式是什麼?

(4)應用程式是如何接收到並處理事件的

在開始敘述各部分的功能之前,我們還是先說說更個Input系統的來龍去脈,一方面能夠知道Input系統從哪兒來,另一方面能對整個系統有個大概的瞭解,使我們不至於迷失在浩瀚的Android原始碼中。在Android系統中一說到重要的服務,基本都是要從systemserver程序開始說起,因為他是Android世界的開拓者,建立了Android世界所需要個基礎。同樣,Input系統也是從systemserver中開始說起,首先建立一個InputManagerService物件,為這個物件設定與WindowManagerService相關的回撥函式,然後呼叫InputManagerService的start函式。

1 inputManager = new InputManagerService(context, wmHandler);
2 inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
3 inputManager.start();

在InputManagerService中start方法會通過JNI呼叫,啟動Native層的InputReaderThread,InputDispatcherThread執行緒,從而開始Input系統的執行。InputReaderThread主要是執行和InputReader相關的內容,主要是從EventHub中讀取事件,預處理事件,然會是根據policy來處理此事件,最後傳送一個訊息到InputDispatcher中通知事件的產生。緊接著InputDispatcher會開始事件的分發,通過InputChannel把事件分發給WindowManager或者應用程式。說以一個事件的流程是從 Eventhub  ---> InputReader  ---> InputDispatcher  ---> InputPublisher  ---> InputChannel  ---> InputConsumer  --->  WindowManager or Application.這就是整個事件分發的大致流程。

  由這個大致的流程開始,我們逐步來解析Android系統Input的內容。從Input的啟動開始,也就是InputManagerService的建立和執行緒的啟動開始。先看InputManagerService的建構函式,程式碼如下:

 1     public InputManagerService(Context context, Handler handler) {//這裡的handler是WindowManagerService處理訊息專用的執行緒,InputManagerService會把訊息傳送到這個執行緒中loop
 2         this.mContext = context;
 3         this.mHandler = new InputManagerHandler(handler.getLooper());//而和InputManagerService相關的訊息的處理時在這個物件中完成的
 4 
 5         mUseDevInputEventForAudioJack =
 6                 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
 7         Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
 8                 + mUseDevInputEventForAudioJack);
 9         mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//通過JNI呼叫來啟動native層的input系統,然後把返回值存放在mPtr中
10     }

從程式碼可以看出,InputManagerService的構造是很簡單的,只是在最後通過JNI方法初始化了native層的Input系統。接下來我們就看看在native層都做了些什麼,程式碼如下:

複製程式碼

 1 static jint nativeInit(JNIEnv* env, jclass clazz,
 2         jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
 3     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
 4     if (messageQueue == NULL) {
 5         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
 6         return 0;
 7     }
 8   //這裡例項化了NativeInputManagerService的一個物件,使用的Java層的MessageQueue的Looper,意味著Java層訊息和Native訊息是在同一個MessageQueue中的
 9     NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
10             messageQueue->getLooper());
11     im->incStrong(0);
12     return reinterpret_cast<jint>(im);//把新建的NativeInputManager強制轉換,返回給Java層
13 }

在native層初始化的時候,建立了一個名叫NativeInputMnager的物件,這個物件是很重要的,因為它主要負責和系統的其他模組互動,而且InputReader和InputDispatcher都是隻執行在Native層中,如果需要呼叫Java函式也是通過這個物件進行的,另外他實現了InputReaderPolicyInterface和InputDispatcherPolicyInterface,是一個重要的Policy。NativeInputManager在構造過程中,完成了InputManager在native基本執行元件的建立,比如建立了EventHub物件,它是事件的Android系統的起源地,所有的事件都是它從驅動中讀取出來的;還建立了InputReaderThread執行緒用來執行InputReader的功能;InputDispatcherThread用來執行InputDispatcher的功能;同時也建立了InputManager來管理EventHub,InputReader,InputReaderThread,InputDispatcher,InputDispatcherThread這些Native執行的基本物件。這些物件的建立過程中並沒有非常重要的呼叫,這裡略過程式碼。不過要注意一點的是NativeInputManager是InputReaderPolicyInterface和InputDispatcherPolicyInterface的子類,因此在構造InputReader和InputDispatcher的時候要用到NativieInputManager物件。

  在物件構建完成後,開始執行start方法,讓之前建立的這些物件執行起來。start方法也是比較簡單的,就是通過JNI呼叫讓native層的Input系統執行起來,然後在Java層把自己列入WatchDog的監視範圍內。之後定義下自己需要接受的外部通知等。這個過程看程式碼的話,比較容易,不再列出。那麼到這裡位置,整個Input系統就執行起來了,至於其中具體的功能我們再逐步分析。這部分內容敘述完畢。

 

(1)InputReader的功能,以及執行的流程

  從前面的內容我們可以知道,在InputManager的start方法被呼叫會,會執行兩個執行緒,分別是InputReaderThread和InputDispatcherThread,雖然它們的啟動在程式碼上有先後之分,但是在實際執行過程中是沒有先後的,所以先從哪個執行緒開始解析Input系統不是很重要的。不過,我是按照從事件的產生到分發開始解析的,所以這裡我是選擇從InputReader開始。InputReader是Android系統中重要的部分,根據Android文件中的描述,主要功能就是:(1) 從EventHub讀取事件,這些事件是元事件,即沒有經過加工或者僅僅是簡單加工的處理的事件;(2)把這些事件加工處理,生成inputEvent事件,這樣封裝之後的事件,可以滿足Android系統的一些需求;(3)把這些事件傳送到事件監聽器,即QueuedInputListener,這個監聽器可以把事件傳遞給InputDispatcher。下面我們就從執行緒開始執行的地方一步一步分析這些功能的實現。既然要看InputReader的功能,我就從InputReader的建構函式說起。前面在說到構造InputManager的時候,就建立了InputReader,當時沒有介紹起功能和構造方法,我們從這裡開始:

 1 InputReader::InputReader(const sp<EventHubInterface>& eventHub,
 2         const sp<InputReaderPolicyInterface>& policy,
 3         const sp<InputListenerInterface>& listener) :
 4         mContext(this), mEventHub(eventHub), mPolicy(policy),
 5         mGlobalMetaState(0), mGeneration(1),
 6         mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
 7         mConfigurationChangesToRefresh(0) {
 8     mQueuedListener = new QueuedInputListener(listener);//在這裡建立了一個QueuedInputListener,注意其引數是listener是InputDispatcher
 9 
10     { // acquire lock
11         AutoMutex _l(mLock);
12 
13         refreshConfigurationLocked(0);
14         updateGlobalMetaStateLocked();
15     } // release lock
16 }

 

在InputReader建立的時候,這裡把InputDispatcher作為引數傳遞進來,然後以InputDispatcher作為引數構造出了QueuedInputListener物件。所以現在有這麼一個關係:InputReader持有一個QueuedInputListener,而QueuedInputListener持有InputDispatcher物件。接下來,我們繼續以執行緒為線索,分析我們的程式碼,接著看

1 bool InputReaderThread::threadLoop() {
2     mReader->loopOnce();
3     return true;
4 }

在這裡補充一點內容: Android系統在Native層中實現了一個類似於Java中的執行緒物件,即C++中的Thread類。這個執行緒類有個特點就是,當執行緒開始執行後,不一直重複執行threadLoop方法,知道這個執行緒的強引用計數變為零為止。所以,這裡的threadLoop函式會不停地執行下去,也即是mReader->loopOnce()會迴圈執行下去,每迴圈一次就能從EventHub中讀取出若干事件。下面我們就以一次迴圈過程為例,分析此執行緒的執行,loopOnce的程式碼如下:

 1 void InputReader::loopOnce() {
 2     int32_t oldGeneration;
 3     int32_t timeoutMillis;
 4     bool inputDevicesChanged = false;
 5     Vector<InputDeviceInfo> inputDevices;
 6     ...
 7     //如果系統剛剛啟動,或者有新的裝置加入的話,timeoutMillis一般為0,意味著無需等待,可以立即返回;timeoutMillis一般為-1,意味著無限等待
 8     size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
 9 
10     { 
11         AutoMutex _l(mLock);
12         mReaderIsAliveCondition.broadcast();
13 
14         if (count) {
15             processEventsLocked(mEventBuffer, count);//開始處理讀取出來的元事件
16         }
17 
18         ...
19     } 
20 
21     if (inputDevicesChanged) {
22         mPolicy->notifyInputDevicesChanged(inputDevices);
23     }
24     //把QueuedInputListener中的訊息全部都開始處理
25     mQueuedListener->flush();
26 }

整個方法的功能就是,從EventHub中讀取出若干事件,然會對這些事件進行預處理,然會把QueuedInputListener中的事件分發出去。這個方法中包含了InputReader的主要功能,所以此執行緒每迴圈一次,都會執行完成一次InputReader的主要功能。先說從EventHub讀取事件功能:

  1.1  從EventHub獲取事件

  先簡單介紹下EvenHub,這個類的主要功能就是主動監視Input驅動的變化,一旦有事件產生,就從產生事件相應的驅動中讀取出這個事件。實現這個監視驅動功能,是通過Linux提供的epoll機制來實現。epoll機制簡單地說就是高效地I/O多路複用機制,使用epoll_wait來監聽所需要的檔案描述符的變化,關於epoll的介紹有很多文章,man中也有詳細的介紹。EventHub的主要功能是通過epoll_wait來實現的,所以EventHub所在的執行緒應該會阻塞在epoll_wait方法中,一直等到epoll_wait設定的超時時間。現在我們開始看看EventHub的實現,在EventHub的建構函式中,建立了一個管道,並把這個管道的讀端和寫端的檔案描述符新增到epoll的監視之下,以便於其他的執行緒或者程序能夠使EventHub所在的執行緒從epoll_wait的阻塞中返回。EventHub在建立完成之後,第一個被呼叫的方法就是getEvents,而且這個方法也是EventHub的主要功能,對於這個方法需要仔細分析,我們把getEvents方法也分成了三個部分去解析,分別是:開啟裝置部分;事件讀取部分;等待部分。這三個部分中,以事件的讀取部分為重點。裝置開啟部分一般發生在Input系統建立的時候呼叫,所以在系統啟動完成,穩定之後,這部分內容應該不會再被執行的;而等待部分較為簡單。不過這些作為系統必不可少的部分,還是要一一說明的,先說裝置開啟部分吧,程式碼如下:

 1 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
 2     ...
 3     struct input_event readBuffer[bufferSize];
 4     //這是元事件指標,可以指向一系列的事件,這些事件按照陣列的方式存放的
 5     RawEvent* event = buffer;
 6     size_t capacity = bufferSize;
 7     bool awoken = false;
 8     for (;;) {
 9         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
10         //mNeedToReopenDevices = false; mClosingDevices = 0;mNeedToSendFinishedDeviceScan = false;mOpeningDevices = 0
11         //mNeedToScanDevices = true
12         if (mNeedToScanDevices) {
13             mNeedToScanDevices = false;
14             scanDevicesLocked();
15             mNeedToSendFinishedDeviceScan = true;
16         }
17     ...

EventHub物件在初始化的時候,有很多變數都已經賦值,所以我把程式碼中判斷不成立的程式碼塊暫時都拿掉了,只留下了在Input系統啟動時候會執行的內容,也就是scanDevicesLocked方法。在這個方法執行之後,肯定會產生一些裝置新增,移除之類的事件,到時候在一一分析。在這個方法中,使用了一個結構體叫RawEvent,使用這個結構體簡單地表明事件發生的基本資訊,程式碼如下:

struct RawEvent {
    nsecs_t when;//事件發生的時間,在getEvents中對於事件時間的處理也是值得關注的
    int32_t deviceId;//產生這個事件對應的裝置的ID,與具體的硬體無關,其數值和裝置開啟的順序有關
    int32_t type;//事件的型別
    int32_t code;//事件對應的事件碼
    int32_t value;//事件的內容
};

RawEvent來自兩種,一種是在開啟裝置時自己賦值,不如裝置的新增,移除等,這些事件對應的RawEvent都是getEvents自己賦值的,便於InputReader處理;還有一種是來自驅動的產生的事件,由驅動產生的這類事件,在內容中有其自己的定義的型別,就是input_event。 getEvents可以根據input_event產生相應的RawEvent便於InputReader處理。這裡要額外說明一點的就是RawEvent的type,如果是由輸入裝置產生的事件,那麼這個type是和輸入裝置本身的特性相關的,下面列舉出Linux中支援的事件型別:

EV_SYN 用於標識獨立的事件,這些獨立的事件時在時間或者空間上是可以分離的,比如在多點觸控中
EV_KEY 用於標識按鍵,按鈕或者類似按鍵的裝置狀態的變化
EV_REL 用於描述 對於軸線相對變化量,如滑鼠向左移動5個單位
EV_ABS 用於描述 對於軸線的絕對變化量, 比如在觸控式螢幕上的觸控點的座標
EV_SW 標識二進位制的開關狀態
EV_LED 表示裝置上的LED是開or關
EV_SND 用於標識傳送聲音到裝置
EV_REP 表示自動重複的裝置
V_FF 用於標識傳送強制要回饋的命令到裝置
EV_PWR 對於Power鍵的一個特殊狀態或者切換輸入
EV_FF_STATUS 用於收到需要強制回饋的裝置狀態
EV_MSC 如果不是這些已存在的狀態,那麼就用這個標識

這個表格來自於Linux核心文件中的Document/input/event-codes.txt,如果以上有翻譯不恰當的地方,可以去參考原文件。上面這些型別是Linux支援的所有的事件型別,一般的一類裝置可以支援這些型別中的一個或幾個。

在Android系統中,常用的裝置由觸控式螢幕,鍵盤或者滑鼠等,這些裝置一般是能夠產生如下型別的事件:

多點觸屏    大多是EV_ABS, EV_KEY, EV_SYN,有的還設定了EV_MSC
鍵盤         EV_KEY, EV_SW
滑鼠        EV_REL, EV_KEY, EV_ABS

 這個表格僅僅是一般性而言,具體情況還需要參考相應的裝置驅動檔案。這裡之所以介紹這些東西,是因為在InputReader在預處理這些事件的時候會使用type這個型別。瞭解了這些之後,繼續看EventHub是如何開啟這些裝置的。 EventHub是通過掃描/dev/input/目錄下所有可用的裝置,然後逐一開啟這些裝置,開啟這些裝置過程中,EventHub又做了一些Input系統必要的工作,比如構造Device物件,把這些裝置加入到epoll的監視佇列中等,時間戳的設定等。在構造Device物件的時候,是通過InputDeviceIdentifier來構造的,主要思路就是通過ioctl函式從內容中讀取出一些必要的資訊,然後把這些資訊經過InputDeviceIdentifier存入Device中,然後再通過ioctl函式測試裝置的屬性,把這些屬性資訊也存入Device中。程式碼如下:

 1 status_t EventHub::openDeviceLocked(const char *devicePath) {
 2     ...
 3     InputDeviceIdentifier identifier;
 4 
 5     // 獲取裝置的名字,如果成功獲取到裝置的名字,把它存入InputDeviceIdentifier中
 6     if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
 7         //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
 8     } else {
 9         buffer[sizeof(buffer) - 1] = '\0';
10         identifier.name.setTo(buffer);
11     }
12     ...
13 
14     //構造EventHub所需要的物件Device,這裡的fd是剛剛開啟的裝置的檔案描述符
15     int32_t deviceId = mNextDeviceId++;//從這裡可以看出,deviceId是與裝置無關的,和開啟順序有關
16     Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
17 
18     // 測試裝置能夠產生的事件的型別,這些事件型別在前文中已經說到過。這裡就是Android支援的事件型別,是Kernel的一個子集
19     ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
20     ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
21     ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
22     ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
23     ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
24     ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
25     ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
26     ...
27     //根據前面獲取到的裝置屬性,檢測裝置是滑鼠,鍵盤,手柄等,然後把這些資訊繼續存入Device
28     if (test_bit(BTN_MOUSE, device->keyBitmask)
29             && test_bit(REL_X, device->relBitmask)
30             && test_bit(REL_Y, device->relBitmask)) {
31         device->classes |= INPUT_DEVICE_CLASS_CURSOR;
32     }
33     ...

 

這部分程式碼,把InputDeviceIdentifier轉化為了Device,因為Device能夠儲存更多的資訊,是EventHub所需要的。在開啟裝置的時候對這些Device完成了初始化。然後就是把這些裝置加入epoll的監視中,程式碼如下:

1 epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)

如此之後,只要裝置有輸入事件的產生,通過epoll就能從阻塞中返回。之後就是設定裝置的硬體時鐘。在報告事件的時候,我們要使用的時鐘是monotonic clock, 這時鐘的特點就是在每次開機的時候初始化為0。事件發生時的時間戳在input系統中使用非常廣泛,而且Input系統會假設事件的時間戳是monotonic的時間點。最後把這些裝置新增到EventHub的一個Vector中,類似如下格式:

deviceId Device*
1 Device*
2 Device*
... ...

這個陣列將會在EventHub中廣泛地使用,經常使用的方式是通過deviceId獲取Device裝置。到這裡,開啟裝置的工作已經完成,而且為EventHub的工作建立了一些有用的變數和陣列等。EventHub中的第一個功能,開啟裝置已經完成。接著我們在看看事件等待部分,最後再說事件的讀取。其實事件的等待部分很簡單,主要的程式碼就一行,如下:

epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

注意程式碼中的最後一個引數timeoutMillis,前面已經說到過,一般來說這個引數是-1,意味著執行緒會在這個地方阻塞,無限等待下去,直到有事件的發生,而在新的裝置加入的時候,這個值為0,意味著可以立即返回。所以,在系統啟動完成後,如果沒有事件發生的話,InputReaderThread執行緒會阻塞在這裡,一直等待事件的發生。最後,我們看看事件的讀取部分,程式碼如下:

 1         bool deviceChanged = false;
 2         while (mPendingEventIndex < mPendingEventCount) {
 3             const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
 4             ...//這裡省略了對於其他的epoll型別的處理。如果是EPOLLIN型別的事件,意味著epoll監視的檔案描述符中有寫入事件,這類事件是輸入事件,
 5             Device* device = mDevices.valueAt(deviceIndex);
 6             if (eventItem.events & EPOLLIN) {//從產生事件的描述符中讀取出事件,放入readerBuffer
 7                 int32_t readSize = read(device->fd, readBuffer,
 8                         sizeof(struct input_event) * capacity);
 9                 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
10                     deviceChanged = true;
11                     closeDeviceLocked(device);
12                 } else if (readSize < 0) {
13                     if (errno != EAGAIN && errno != EINTR) {
14                         ALOGW("could not get event (errno=%d)", errno);
15                     }
16                 } else if ((readSize % sizeof(struct input_event)) != 0) {
17                     ALOGE("could not get event (wrong size: %d)", readSize);
18                 } else {
19                     int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
20             //在裝置上產生的事件的個數
21                     size_t count = size_t(readSize) / sizeof(struct input_event);
22                     for (size_t i = 0; i < count; i++) {
23                         const struct input_event& iev = readBuffer[i];
24                         ...//這裡省略了對於事件時間戳的設定,考慮的因素挺多,雖時間戳對於輸入事件很重要,但是不應該是本次討論的重點
25                         event->when = now;
26                         event->deviceId = deviceId;
27                         event->type = iev.type;
28                         event->code = iev.code;
29                         event->value = iev.value;
30                         event += 1;
31                     }
32                     capacity -= count;
33                     if (capacity == 0) {
34                         mPendingEventIndex -= 1;
35                         break;
36                     }
37                 }
38             } 
39             ...
40         }

其實這段程式碼也是非常簡單的,基本過程就是,監視到有事件的產生,把事件讀取出來,不過這裡讀出的事件是input_event型別的,然後在逐個把input_event事件轉化為InputReader需要的RawEvent型別的事件,放入InputReader提供給EventHub的陣列中(通過getEvents引數傳遞進來的)。說起來很簡單,其實也很簡單。上面這些程式碼就是讀取事件的核心部分。總結一下,EventHub負責開啟/dev/input/目錄下的所有裝置,然後為每一個裝置建立一個Device,並把這個Device放入EventHub所定義的陣列們Device中。之後,就是把這個裝置納入監視範圍。然後就是開始等待事件的發生,一旦有事件發生,就從產生事件的裝置中讀取出這些裝置,把這些事件轉化為RawEvent型別放入InputReader提供的事件陣列中,之後返回。到這裡,從EventHub獲取事件就結束了。

  1.2  InputReader對元事件的處理

   由上節的內容,我們知道,從EventHub獲得的事件有兩種,一種是裝置新增,移除類的;另一種是由輸入裝置產生的事件。InputReader在處理這兩類事件稍微有點不一樣。先看裝置新增型別的事件,這些新增裝置事件的處理,為InputReader的工作打下了基礎,因為InputReader可以根據新增的裝置定義一些資料結構,為以後處理由此裝置產生的事件打下基礎。接著我們從程式碼開始看看InputReader對於元事件的處理: 

 1 void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
 2     for (const RawEvent* rawEvent = rawEvents; count;) {
 3         int32_t type = rawEvent->type;
 4         size_t batchSize = 1;
 5         if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
 6             int32_t deviceId = rawEvent->deviceId;
 7             while (batchSize < count) {
 8                 if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
 9                         || rawEvent[batchSize].deviceId != deviceId) {
10                     break;
11                 }
12                 batchSize += 1;
13             }
14             //有輸入裝置產生的事件,在這個方法中處理
15             processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
16         } else {
17             switch (rawEvent->type) {//裝置新增類的事件在這裡處理
18             case EventHubInterface::DEVICE_ADDED:
19                 addDeviceLocked(rawEvent->when, rawEvent->deviceId);//這個方法中建立了InputReader所必須的一些資料結構
20                 break;
21             case EventHubInterface::DEVICE_REMOVED:
22                 removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
23                 break;
24             case EventHubInterface::FINISHED_DEVICE_SCAN:
25                 handleConfigurationChangedLocked(rawEvent->when);
26                 break;
27             default:
28                 ALOG_ASSERT(false); // can't happen
29                 break;
30             }
31         }
32         count -= batchSize;
33         rawEvent += batchSize;
34     }
35 }

 先從裝置新增類的事件說起,看看在新增裝置的時候,都建立了那些資料結構。對於addDeviceLocked的原始碼,這裡就不列舉出來,主要說說在InputReader在功能實現時用的變數有那些,分別是是InputDevice,InputMapper。InputDevice代表輸入裝置的一個狀態;InputMapper是某一類事件是如何處理的;兩者之間的關係是,一個InputDevice可以產生多種型別的事件,因此他可以對應多個InputMapper。另外,在InputReader中也儲存了一個vector來儲存InputDevice,這個Vector的名字也叫mDevices,和EventHub中的mDevices類似,不過儲存的內容有些不同。在InputReader的mDevices中儲存的<id, InputDevice*>,而在EventHub中儲存的是<id, Device*>,不過兩者的id是一致的,而且每個InputDevice都是通過Devices來構造的。能夠完成加工RawEvent工作的還是通過不同的InputMapper來完成的,這些InputMapper根據Android系統支援的型別分成了一下幾類,

InputMapper型別 能夠處理的事件的型別
SwitchInputMapper EV_SW, EV_SYN
KeyboardInputMapper EV_KEY, EV_SYN, EV_MSC
CursorInputMapper EV_KEY, EV_SYN, EV_REL
TouchInputMapper EV_KEY, EV_SYN, EV_REL
SingleTouchInputMapper EV_KEY, EV_SYN, EV_REL, EV_ABS
MultiTouchInputMapper EV_KEY, EV_SYN, EV_REL, EV_ABS
JoyStickInputMapper EV_ABS, EV_SYN
VibratorInputMapper --

這裡就基本完成了對於新增裝置類的事件的處理,接下來就看是分析對於輸入裝置產生的元事件的處理。對於輸入事件的處理主要是通過方法processEventsForDeviceLocked進行的,在這個方法執行之前,已經找到了產生這個事件的輸入裝置了,然後把輸入裝置作為引數傳遞進去,processEventsForDeviceLocked方法根據deviceId找到相應的InputDevice,然後呼叫InputDevice的process方法進行處理這個事件。下面,結合InputDevice的process方法的這段程式碼,我們一起看看輸入事件是如何處理的,程式碼如下:

 1 void InputDevice::process(const RawEvent* rawEvents, size_t count) {
 2     size_t numMappers = mMappers.size();
 3     for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
 4             ...//省略了一些與判斷的處理,留下核心部分的程式碼
 5             for (size_t i = 0; i < numMappers; i++) {
 6                 InputMapper* mapper = mMappers[i];
 7                 mapper->process(rawEvent);//讓各個InputMapper去處理元事件,注意引數還是RawEvent型別
 8             }
 9     }
10     
11 }

在這個方法中,注意有內外兩個迴圈,外迴圈是逐一取出元事件,內迴圈是讓每一個InputMapper都處理這個事件。之所以讓每一個InputMapper都進行處理元事件,而不是隻要對應的InputMapper去處理,是因為擔心只讓對應的InputMapper處理元事件會產生副作用,比如For example, joystick movement events and gamepad button presses are handled by different mappers but they should be dispatched in the order received. 對於每一個InputMapper都要處理元事件,我們不做一一分析,僅僅拿出典型的鍵盤輸入事件分析。處理過程如下:

 1 void KeyboardInputMapper::process(const RawEvent* rawEvent) {
 2     switch (rawEvent->type) {
 3     case EV_KEY: {
 4         int32_t scanCode = rawEvent->code;
 5         int32_t usageCode = mCurrentHidUsage;
 6         mCurrentHidUsage = 0;
 7 
 8         if (isKeyboardOrGamepadKey(scanCode)) {
 9             int32_t keyCode;
10             uint32_t flags;
11             if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
12                 keyCode = AKEYCODE_UNKNOWN;
13                 flags = 0;
14             }
15             processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
16         }
17         break;
18     }
19  ...//省略了對於其他事件型別EV_SYN, EV_MSC的處理程式碼
20 }
21 
22 
23 void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
24         int32_t scanCode, uint32_t policyFlags) {
25     ...//省略了對於元事件處理過程的程式碼,主要就是發生事件,事件程式碼,掃描碼,是按下還是彈起,
26     //總之,用於構建下面NotifyKeyArgs的引數大都是在這裡獲取的。
27     NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
28             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
29             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
30     getListener()->notifyKey(&args);
31 }

這是InputReader對於元事件處理的過程。在處理完成後,在最後呼叫了一個重要的方法getListener()->notifyKey(&args)方法。在InputReader處理各種元事件的時候,基本過程都是這樣的,把元事件中的各項資訊構建一個NotifyArgs,然後通過QueuedInputListener來通知InputDispatcher。由此,InputReader的處理過程開始進入了和InputDispatcher互動的階段。其實在QueuedInputListener中對於notifyKey的實現非常簡單,僅僅是把這些事件的引數壓入佇列而已,並沒有做太多的操作就返回了。

  1.3   InputReader把事件傳送到InputDispatcher

   前面我們已經知道了,InputReader把元事件處理完畢後,構造了一個NotifyArgs,並把這個物件壓入了QueuedInputListener的佇列中,然後就返回了。當時我們並不知道如何把這些佇列中的事件傳送的InputDispatcher中的。這裡,就給出了這個過程。InputReader呼叫QueuedInputListener的flush方法,把QueuedInputListener佇列中的所有事件都發送到InputDispatcher中。下面我們就分析這個過程,從QueuedInputListener的flush方法說起,程式碼如下:

1 void QueuedInputListener::flush() {
2     size_t count = mArgsQueue.size();//前面,我們就是把NotifyArgs放入了mArgsQueue中
3     for (size_t i = 0; i < count; i++) {
4         NotifyArgs* args = mArgsQueue[i];
5         args->notify(mInnerListener);//逐個取出NotifyArgs,然後呼叫notify方法,注意這裡面的引數是mInnerListener,是InputDispatcher
6         delete args;
7     }
8     mArgsQueue.clear();
9 }

這裡從佇列中逐個取出NotifyArgs,然後呼叫他們的notify方法。在QueuedInputListener建立的時候,我們傳入建構函式的的引數是一個InputDispatcher,在這裡就使用到了,把這個InputDispatcher作為引數向下傳遞。在NotifyArgs的notify方法中,基本都類似於

1 62void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
2 63    listener->notifyKey(this);//呼叫InputDispatcher的對應的方法。
3 64}

  到這裡,我們對於InputReader的功能的分析就完成了。總結一下,基本過程說就是:InputReader從EventHub中讀取出來元事件,預處理加工這些元事件成為NotifyArgs,然後通過QueuedInputListener把他們通知給InputDispatcher。整個Input的流程圖太大,在這裡顯示不完全。現在僅僅拿出,和InputReader功能相關的部分的流程圖,圖中是以一個鍵盤事件的處理過程。圖如下:

2.   InputDispatcher的功能和流程

   在開始介紹InputDispatcher的功能之前,先看看Android文件對於其功能的描述:把輸入事件傳送到他的目標中去。他的目標可能是應用程式,也可能是WindowManagerService。如果是應用程式的話,可以通過registerInputChannel來定義輸入事件的目標。我們已經瞭解InputDispatcher的唯一一個功能就是分發事件。知道了其功能之後,我們就開始分析InputDispatcher是如何實現這些功能的吧。先看他的建構函式,InputDispatcher建立了一個Looper,程式碼如下:

1  mLooper = new Looper(false);

這意味著,InputDispatcher有自己的Looper,沒有和別人共用,資訊也是自己在迴圈的。這個Looper是native層的Looper,由C++程式碼實現。在構建Looper過程中,新建了一個管道,這個管道僅僅起到了喚醒Looper,讓其能從阻塞等待中返回。Looper中建立的管道是實現Looper功能的重要的方式,是通用的,不是僅僅為了InputDispatcher準備的。看完建構函式之後,我們接著分析InputDispatcher的功能,接著上節中的QueuedInputListener通知InputDispatcher有新的按鍵事件說起。這裡還是接著上面的以按鍵的處理,接著看InputDispatcher是如何實現分發的,程式碼如下:

 1 void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
 2     ...
 3     KeyEvent event;//在這裡通過傳遞進來的NotifyArgs為引數,構建KeyEvent
 4     event.initialize(args->deviceId, args->source, args->action,
 5             flags, args->keyCode, args->scanCode, metaState, 0,
 6             args->downTime, args->eventTime);
 7   //通過NativeInputManager把這個KeyEvent最終傳遞給WindowManagerService去處理
 8     mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
 9     ...
10     bool needWake;
11     ...
12         int32_t repeatCount = 0;//這下面構建KeyEntry
13         KeyEntry* newEntry = new KeyEntry(args->eventTime,
14                 args->deviceId, args->source, policyFlags,
15                 args->action, flags, args->keyCode, args->scanCode,
16                 metaState, repeatCount, args->downTime);
17 
18         needWake = enqueueInboundEventLocked(newEntry);
19         mLock.unlock();
20 
21     if (needWake) {//喚醒等待Looper
22         mLooper->wake();
23     }
24 }

我們先從程式碼中的line 8開始,這行程式碼的意思是,把KeyEvent傳送出去,至於目的地是哪兒,在InputDispatcherPolicy中會有決定。是NativeInputManager實現了這個Policy,所以程式碼的執行會進入NativeInputManager中。

 事件在入隊前(before enqueue)的處理

    在文章的前面已經說到過,NativeInputManager負責和系統的其他模組互動--是其功能之一。把這個KeyEvent傳遞到NativeInputManager之後,繼續分發,最終會把這個KeyEvent傳遞到PhoneWindowManager中去處理這個事件,傳遞過程如下:NativeInputManager->interceptKeyBeforeQueueing  ----> InputManagerService.interceptKeyBeforeQueueing ----> InputMonitor.interceptKeyBeforeQueueing ---->  PhoneWindowManager.interceptKeyBeforeQueueing.大致過程是這樣的,具體細節不再贅述。在傳遞過程中是跨執行緒的。通過這一系列的方法的名字可以看出,是在事件進入InputDispatcher的佇列之前,進行的一些處理。在PhoneWindowManager處理事件之後,會有一個返回值來標記這一事件處理的結果是怎樣的,為後面的事件進入佇列做準備。在PhoneWindowManager對事件進行前期的攔截處理過程時,一般首先把事件都標記上PASS_TO_USER,即這個事件要交給應用程式去處理,但是在處理過程中決定,有些事件是沒必要傳遞給應用程式的,比如:在通過過程中按下音量相關的事件,結束通話電話的事件,power鍵的處理,以及撥打電話的事件。這些事件的處理結果都是不必傳遞到應用程式的,這個結果最為返回值,最終會一步一步地返回到NativeInputManager中,這個返回值會作為NativeInputManager的policyFlags的一部分,供InputDispatcher使用。在PhoneWindowManager對事件處理完成後,才會把這個事件構造成為一個形式為EventEntry放入佇列。到這裡,我們的工作仍在InputReaderThread的執行緒中,雖然是對InputDispatcher的操作。接下來才是真正進入InputDispatcherTread執行緒對InputDispatcher操作。通過InputDispatcher的mLooper的wake方法,喚醒InputDispatcherThread執行緒。關於Looper如何在wake時是如何通過管道的方式去實現的,這個過程應該放在一篇單獨的文章中詳細地去說明,在以後的文章中,我會說到Looper在native實現時的一些特點的。這裡,我們知道InputDispatcherThread執行緒被喚醒了。如果你已忘記InputDispatcherThread執行緒是何時被阻塞,那就回頭再重新看看吧。學習別人的思路就是這樣,反覆回頭看,才能不至於迷失在別人的思維中。然後就開始執行InputDispatcher的threadLoop函式,之後就呼叫InputDispatcher的dispatchOnce方法,程式碼如下:

 1 void InputDispatcher::dispatchOnce() {
 2     nsecs_t nextWakeupTime = LONG_LONG_MAX;//應該是64位二進位制所能表示的最大值,大概是2^63-1,即9223372036854775807
 3     { 
 4         AutoMutex _l(mLock);
 5         mDispatcherIsAliveCondition.broadcast();
 6     //如果沒有等待執行的命令的話,就開始一次迴圈分發。在迴圈過程中,可能會有一些命令產生。這裡的命令大概是模式設計中的:命令模式吧
 7         if (!haveCommandsLocked()) {
 8             dispatchOnceInnerLocked(&nextWakeupTime);
 9         }
10     //如果任何等待執行的命令的話,那麼就執行這些命令;假如有命令已經執行了,那麼下次poll的時候直接喚醒
11         if (runCommandsLockedInterruptible()) {
12             nextWakeupTime = LONG_LONG_MIN;//#define LONG_LONG_MIN  (-__LONG_LONG_MAX__-1LL), 即-9223372036854775808
13         }
14     } // release lock
15 
16     nsecs_t currentTime = now();
17     int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
18     mLooper->pollOnce(timeoutMillis);
19 }

 InputDispatcher的主要功能就在這段程式碼中,這是個輪廓。要想知道具體的功能的實現,還要需要逐步分析下去。先看line7和line8中的程式碼,如果是一次正常的分發迴圈(dispatch loop)的話,應該是沒有等待執行的命令。為什麼會沒有等待執行的命令,在後面會說到原因,先不要著急。所以接下就開始dispatchOnceInnerLocke方法,從這個方法的名字可以看出,這應該是功能的核心實現部分。看其程式碼是如何實現的:

 1 void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
 2     nsecs_t currentTime = now();
 3    //如果等待處理的事件不存在的話
 4     if (! mPendingEvent) {
 5         if (mInboundQueue.isEmpty()) {
 6             ...//省略了,當等待處理事件不存在且事件佇列為空的時候的處理
 7         } else {//從事件佇列的頭部取出一個事件
 8             mPendingEvent = mInboundQueue.dequeueAtHead();
 9             traceInboundQueueLengthLocked();
10         }
11         if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
12           //通知某些Activity一些事件的發生,通過這個方法的名字可以聯想一下,一些社交網站中的“捅一下”應用,或者QQ中的震動視窗功能,
13             pokeUserActivityLocked(mPendingEvent);//這個方法的功能就類似於那些作用。只不過這裡主要是用來“捅一下”PowerManagerService的
14         }
15 
16         // Get ready to dispatch the event.
17         resetANRTimeoutsLocked();
18     }
19   //現在我們有事件需要開始處理了
20     ALOG_ASSERT(mPendingEvent != NULL);
21     bool done = false;
22     DropReason dropReason = DROP_REASON_NOT_DROPPED;//在開始處理之前,所有的事件都不必丟棄
23     if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
24         dropReason = DROP_REASON_POLICY;
25     } else if (!mDispatchEnabled) {
26         dropReason = DROP_REASON_DISABLED;
27     }
28 
29     if (mNextUnblockedEvent == mPendingEvent) {
30         mNextUnblockedEvent = NULL;
31     }
32 
33     switch (mPendingEvent->type) {
34 ...//省略了對於config change類別的事件的處理
35 ...//省略了對於裝置重置事件的處理
36     case EventEntry::TYPE_KEY: {
37         KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
38         if (isAppSwitchDue) {//下面這些內容,是對於事件是否需要丟棄的分析
39             if (isAppSwitchKeyEventLocked(typedEntry)) {
40                 resetPendingAppSwitchLocked(true);
41                 isAppSwitchDue = false;
42             } else if (dropReason == DROP_REASON_NOT_DROPPED) {
43                 dropReason = DROP_REASON_APP_SWITCH;
44             }
45         }
46         if (dropReason == DROP_REASON_NOT_DROPPED
47                 && isStaleEventLocked(currentTime, typedEntry)) {
48             dropReason = DROP_REASON_STALE;
49         }
50         if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
51             dropReason = DROP_REASON_BLOCKED;
52         }//無論事件是否要被丟棄,都要經過如下的處理
53         done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
54         break;
55     }
56     ...//省略了對於motion事件的處理
57     }
58     ...
59 }

這個方法中的大部分功能都已經在程式碼中註釋了,主要就是取出事件,分析是否需要丟棄,然後就是開始按照型別分發事件,我們假設的是按鍵事件,所以接下來就是呼叫dispatchKeyLocked方法來分發。

 1 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
 2         DropReason* dropReason, nsecs_t* nextWakeupTime) {
 3     if (! entry->dispatchInProgress) {
 4         ...//省略了對於重複事件在各種情況下的處理
 5     }
 6 
 7    ...//在入佇列之前,對於事件有個一次intercept,這裡是對事件的intercept結果的處理
 8     Vector<InputTarget> inputTargets;
 9     int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
10             entry, inputTargets, nextWakeupTime);//尋找事件傳送到的目標視窗
11      // 分發事件
12     dispatchEventLocked(currentTime, entry, inputTargets);
13     return true;
14 }

這個方法中主要就是尋找到事件應該分發到的目標,可能是應用視窗.這個目標應用的視窗尋找與應用程式啟動時設定到視窗有關。在下一小節中會說到這個視窗是如何找到的。其程式碼不是很複雜,自己看看的話也很容易能夠明白。其他的內容在上面的註釋中也有說明。下面還是將注意力集中在事件分發上,注意這裡傳入dispatchEventLocked的引數中inputTargets是複數,也就是說可能有多個目標。所以在方法dispatchEventLocked中就是根據每一個target對應的inputChannel找到connection,然後 prepareDispatchCycleLocked使用這個connection把事件逐個分發到target中。 在prepareDispatchCycleLocked方法中,主要就是根據事件是否可以分割,分別把事件放入佇列。在入佇列的之後,InputPublisher的釋出事件的佇列就不再為空,然後會呼叫 startDispatchCycleLocked方法,通過InputPublisher開始釋出事件。大致過程如此,為了減少篇幅,這裡就不再列出程式碼了。流程圖如下:

 

整個的流程圖太大了,不太方便,這裡僅僅是其中的一部分。說明一點:圖中Looper到InputDispatcher中的dispatcherOnce不是呼叫關係,只是Looper把其所在的程序即InputDispatcherThread執行緒給喚醒,所以開始執行dispatchOnce。這裡到最後就是呼叫InputPublisher的publishKeyEvent方法,把事件釋出出去。在前面我們說到過這麼一個問題,等待執行的命令為什麼在一次正常的事件分發之後應該為空?這些命令產生的地方分別在pokeUserActivity方法中, 和dispatchKeyLocked中等等在使用postCommand把命令放入佇列的地方。在上面這個過程執行完畢後,會返回到dispatchOnce方法中,接著往下執行,也就是執行程式碼:

runCommandsLockedInterruptible()

也就是前面dispatchOnce方法的line 11. 這個方法的功能就是執行之前放入命令佇列的命令。具體的程式碼不再列出。到這裡,關於InputDispatcher的功能--唯一的一個功能--事件分發,就算介紹完了。

3   通訊方式

  從這節開始介紹Input子系統是如何實現通訊的。其實InputReaderThread與InputDispatcherThread之間, InputDispatcherThread和WindowManagerService所線上程之間的通訊是相對簡單的,因為他們在相同的程序---systemServer中,因此可以通過使用同一個物件就可以完成通訊。這裡就不多做介紹。主要是分析InputDispatcherThread與應用程式之間傳遞事件時的通訊----socket通訊,以及Input和應用程式是如何利用socket方式完成事件的傳遞的。

  在開始之前,還是再續點閒話吧,要不然直接開始下面的分析,會讓人覺得很突兀。我之所以能找到思路從下面的這個節點分析,是因為在寫這篇文章之前,我已經對Input系統有了大概的瞭解了,對於其中的通訊方式也有了瞭解的。從通訊方式的建立,反推一步一步地找到了ViewRootImpl中的。在文章中沒有按照我尋找線索的方式去寫,因為我覺得那麼寫的話有點混亂,而且要時刻保持緊張的心態去分析,太累。所以才能這麼開始的,希望能夠獲得理解。在每個Activity建立的時候,都會擁有其相應的ViewRootImpl。這個知識點在網路上很多文章分析Activity的啟動過程中都會詳細描述的,這裡不再贅述。ViewRootImpl就代表一個Activity建立能夠接收事件的渠道。這個建立過程在ViewRootImpl的setView中。在setView中的程式碼很多,功能也需要仔細分析,這裡僅僅列出和Input相關的程式碼,如下:

 1     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
 2         synchronized (this) {
 3             if (mView == null) {
 4                 //如果這個Window的屬性中設定了不再需要InputChannel,那麼就可以不用建立InputChannel。
 5                 //我們是需要一個InputChannel的。
 6                 if ((mWindowAttributes.inputFeatures
 7                         & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
 8                     mInputChannel = new InputChannel();
 9                 }
10                 try {
11                     mOrigWindowType = mWindowAttributes.type;
12                     mAttachInfo.mRecomputeGlobalAttributes = true;
13                     collectViewAttributes();
14                     //這裡是把InputChannel最終傳遞到WindowManagerService中,用於傳遞渠道的建立
15                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
16                             getHostVisibility(), mDisplay.getDisplayId(),
17                             mAttachInfo.mContentInsets, mInputChannel);
18                 } 
19                 ...    
20                 //DecorView是RootViewSurfaceTaker的一個例項,
21                 if (view instanceof RootViewSurfaceTaker) {
22                 //雖然這行程式碼會被執行,但是得到的最終值還是null。在整個程式碼中,我並沒有找到InputQueueCallback物件建立的地方
23                     mInputQueueCallback =
24                         ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
25                 }
26                 if (mInputChannel != null) {
27                     if (mInputQueueCallback != null) {
28                         mInputQueue = new InputQueue(mInputChannel);
29                         mInputQueueCallback.onInputQueueCreated(mInputQueue);
30                     } else {
31                         //這裡建立了一個WindowInputEventReceiver,注意引數是前面建立的InputChannel和本Activity所線上程的Looper,
32                         mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
33                                 Looper.myLooper());
34                     }
35                 }
36             ...
37             }
38         }
39     }

這段程式碼就是我們分析通訊機制在應用端建立的輪廓。後面的大部分內容都是基於這段程式碼分析進行的,只不過是這段程式碼的層層深入而已。在之前,我們一直沒有介紹在事件傳遞中一個重要的類InputChannel,這裡就詳細說明下。在Native層的InputChannel就是一個通道,僅僅是一個通道,僅僅具有通訊功能,不包含其他的。至於從資料流動方向,與InputChannel無關。資料流向是有InputPublisher和InputConsumer在組合了InputChannel後決定的。先看在程式碼line 8中,建立一個InputChannel例項,它是一個Java物件,通過它的建構函式可以看出,只是建立了一個物件,並沒有進行任何例項化的操作。之後,就是把這個物件作為引數傳遞到了WindowManagerService中,有addWindow來使用。把InputChannel由應用程式傳遞到WindowManageService的過程,涉及到的是Binder通訊,不是文章的重點,不多說。需要知道的是,mWindowSession.addToDisplay最後會傳遞到WindowManagerService的addWindow方法。通過程式碼看看InputChannel是如何使用的,程式碼如下:

 1 public int addWindow(Session session, IWindow client, int seq,
 2             WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
 3             Rect outContentInsets, InputChannel outInputChannel) {
 4             ...
 5             if (outInputChannel != null && (attrs.inputFeatures
 6                     & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
 7                     //這個名字是根據物件的hashcode和視窗的一些屬性轉化為字串後建立的。
 8                 String name = win.makeInputChannelName();
 9                 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);            
10                 win.setInputChannel(inputChannels[0]);
11                 inputChannels[1].transferTo(outInputChannel);            
12                 mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
13             }
14             ...
15     }