Linux內核提供了一個Input子系統來實現的,Input子系統會在/dev/input/路徑下創建我們硬件輸入設備的節點,一般情況下在我們的手機中這些節點是以eventXX來命名的,如event0,event1等等,可以利用EVIOCGNAME獲取此事件結點名稱。這就是Android中對於input事件處理數據的來源點,至於驅動寫入數據這塊就不說了。
首先,簡而言之的介紹一下android事件傳遞的流程,按鍵,觸屏等事件是經由WindowManagerService獲取,並通過共享內存和管道的方式傳遞給ViewRoot,ViewRoot再dispatch給Application的View。當有事件從硬件設備輸入時,system_server端在檢測到事件發生時,通過管道(pipe)通知ViewRoot事件發生,此時ViewRoot再去的內存中讀取這個事件信息。下面以一個模塊劃分圖了解一下整個過程:
下面詳細介紹一個各個模塊主要處理流程:
1、建立通讀通道初始化:
A、WindowManagerService與ViewRoot建立管道初始化
WindowManagerService : 主要負責事件傳遞,運行於system_server中,主要利用inputmanager啟動input事件啟動線程
讀取event數據
WindowManagerService--->ViewRoot方向的管道通信,表示WMS通知ViewRoot有新事件被寫入到共享內存;
ViewRoot-->WindowManagerService方向的管道通信,表示ViewRoot已經消化完共享內存中的新事件,特此通知WMS。
ViewRoot和WindowManagerService的管道的文件描述符都是被存儲在一個名為InputChannel的類中,這個InputChannel類是
管道通信的載體。而這兩者間通過ashmem_create_region創建匿名內存進行數據的傳遞 。
ViewRoot.java 端建立管道:
mInputChannel = new InputChannel();
try {
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e)
WindowManagerService.java 建立管道:
if (outInputChannel != null) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.mInputChannel = inputChannels[0];
inputChannels[1].transferToBinderOutParameter(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel);
}
創建一對InputChannel,這一對InputChannel中實現了一組全雙工管道及申請共享內存,其中outInputChannel為ViewRoot傳遞來的
InputChannel對象,在addWindow中對其進行賦值。如此兩者利用pipe建立控制通道,利用共享內存建立數據通道。
這是涉及到的文件如下:
frameworks/base/service/java/com/android/server/WindowManagerService.java
frameworks/base/core/java/android/view/ViewRoot.java
--> jni android_view_InputChannel.cpp
--> InputTransport.cpp
B、InputChannel的注冊過程
一個管道通信只是對應一個Activity的事件處理,也就是當前系統中有多少個Activity就會有多少個全雙工管道,那麼系統需要一個管理者來管理以及調度每一個管道通信,因此我們在創建完InputChannel對象後,需要將其注冊到這個InputManager管理中。
采用Looper來輪詢是否有事件發生,InputManager啟動了2個進程來管理事件發生與傳遞,InputReaderThread和InputDispatcherThread,InputReaderThread進程負責輪詢事件發生; InputDispatcherThread負責dispatch事件。