Android2.3的輸入事件流程與以前版本有了較大的不同,這裡做一下詳細的分析,最後我把自己分析時用的示範代碼放在了這裡:
http://code.google.com/p/flying-on-android/
下面的分析都是基於這些源碼的,大家可以下載下來一邊看源碼一邊看文檔。源碼裡只要關注FlyingEvent這個類就可以了。如果只想看一下示範結果,可以直接把包裡的flying放到機器的/system/bin目錄執行,開啟logcat後就可以看到示範輸出。運行程式時,機器螢幕會有異象產生,很正常,因為這個程式原本是用於顯示SurfaceFlinger的,這次為了示範EventHub稍微改了一下。大家只要關注FlyingEvent.cpp這個檔案就好了。
大家也可以用源碼自己編譯出示範程式,只要把解壓後的flying檔案夾放到/frameworks/base/cmds/目錄下,然後切換到flying目錄下使用mm編譯。
先大致介紹一下整個流程,再做重點分析。輸入事件流程一共涉及到下面這幾個檔案:
/frameworks/base/services/java/com/android/server/WindowManagerService.java
/frameworks/base/services/java/com/android/server/InputManager.java
/frameworks/base/services/jni/com_android_server_InputManager.cpp
/frameworks/base/libs/ui/InputReader.cpp
/frameworks/base/libs/ui/InputDispatcher.cpp
/frameworks/base/libs/ui/EventHub.cpp
其中,WindowManagerService.java和InputManager.java主要向Android為視窗系統提供服務,EventHub.cpp主要用來讀取裝置檔案中的RawEvent,而InputReader.cpp和InputDispatcher.cpp算是它們之間的對接層。
它們的關係是:WindowManagerService通過InputManager提供的介面開啟一個線程驅動InputReader不斷地從/dev/input/目錄下面的裝置檔案讀取事件,然後通過InputDispatcher分發給串連到WindowManagerService服務的用戶端。
InputReader從裝置檔案中讀取的是RawEvent,在交給InputDispatcher進行分發之前,它需要先把RawEvent進行轉化分類,拆分成KeyEvent、MotionEvent、TrackEvent各種類型等。這篇文章主要關注的就是這個RawEvent的拆分過程,所以我們的重點在EventHub.cpp中。並且,為了簡單化分析過程,在這裡我的分析只關注觸控螢幕事件。看它是如何從RawEvent被拆分成應用程式層使用者事件MotionEvent的。
看下面的分析之前,最好先去上面提到的地址把源碼下載下來,參照裡面的FlyingEvent.cpp。
整個過程大致分成這麼幾步:
一、初始化。
先new一個EventHub的執行個體:mEventHub(new EventHub),
接下來,開啟一個線程通過mEventHub不停地從裝置檔案中讀取RawEvent並處理:
while (1) {
RawEvent event;
mEventHub->getEvent(&event);
process(event);
}
EventHub在初始化的時候做一些事情,
1、搜尋當前的輸入裝置每搜尋到一個就會產生一個類型為DEVICE_ADDED的事件,當讀取這種RawEvent時,InputReader會把搜尋到的這個裝置記錄下來。
2、如果搜尋到了鍵盤時,就會載入鍵盤配置檔案。載入完成後產生一個類型為FINISHED_DEVICE_SCAN的事件。這樣,後邊從驅動讀取使用者按鍵時,就會去載入的鍵盤配置檔案中尋找映射的索引值封裝成KeyEvent返回給使用者。
二、EventHub初始化完畢後,就開始等待使用者輸入。線程一直阻塞在mEventHub->getEvent(&event),直到有使用者事件產生才會返回。
當有一個事件產生時,傳遞給process進行處理。
三、事件拆分
FlyingEvent.process裡面主要調用了FlyingEvent.consume方法來處理使用者事件。這裡只分析touch事件。touch事件可以分為三種:down,move,up。
down類型的touch事件需要四個RawEvent來完成,第一個是X座標(ABS_X),第二個是Y座標(ABS_Y),第三個代表方向(ABS_PRESSURE)(0的時候是up,1的時候是down,所以這裡應該是1),第四個是結束標誌(SYN_REPORT)。
move類型的touch事件需要三個RawEvent來完成,第一個是X座標,第二個是Y座標,第三個是結束標誌。
up類型的touch事件需要兩個RawEvent來完成,第一個代表方向(0的時候是up,1的時候是down,所以這裡應該是0),第四個是結束標誌。
可能你已經注意到了up事件是沒有座標資訊的,它的座標資訊與down(沒有move時)或最後一個move(down和up之間有move事件產生)事件的座標相同。
從FlyingEvent.consume方法中,每一個事件最終都會產生一個TouchEvent,然後調用printTouchEvent進行列印,最後把它儲存到eventBuffer中。
參考文章
李先靜的“Android輸入事件流程“,不過使用的Android版本比較老了。
http://blog.csdn.net/absurd/archive/2009/05/17/4195363.aspx