前言
本文代碼基于 Android 12 。
概述
Android 的事件輸入可以簡化為三部分:
- 物理輸入設備 -> InputDispatcher
- InputDispatcher -> ViewRootImpl
- ViewRootImpl 事件派發(fā)
物理輸入設備 -> InputDispatcher
這一部分主要有三個流程:
- 物理輸入設備 -> 標準的 Linux 輸入事件
- 標準的 Linux 輸入事件 -> Android 輸入事件
- Android 輸入事件 -> Android 顯示器上的窗口
物理輸入設備 -> 標準的 Linux 輸入事件
這一部分主要由 Linux 內核中的輸入設備驅動程序完成:
物理設備生成輸入事件信號后,會由設備固件編碼成設備特有的事件信號,并傳輸給 Linux 系統(tǒng),再由 Linux 內核中的輸入設備驅動程序解碼成標準的 Linux 輸入事件格式。
標準的 Linux 輸入事件 -> Android 輸入事件
這一部分主要由 Android InputReader 實現(xiàn):
Android EventHub 組件會打開與每個輸入設備關聯(lián)的 evdev 驅動程序并從 Linux 內核中讀取這些標準的輸入事件,再由 Android InputReader 組件根據設備類別解碼成 Android 定義的事件。
Android 輸入事件 -> Android 顯示器上的窗口
該部分主要由 Android InputDispatcher 組件完成:
Android InputReader 會把生成的 Android 輸入事件流發(fā)送給 InputDispatcher ,然后由 InputDispatcher 將這些事件轉發(fā)給對應的窗口。
如圖:

根據物理輸入設備的不同,Android 輸入事件主要分為 KeyEvent 和 MotionEvent 。

所以用來上傳 KeyEvent 的設備有物理鍵盤設備、DPad 等。而上傳 MotionEvent 的設備有顯示屏設備、鼠標、手寫筆等。
InputDispatcher -> ViewRootImpl
InputDispatcher 是怎么把事件轉發(fā)給對應的窗口呢?
答:app 在向系統(tǒng)添加窗口時,會和系統(tǒng)進程建立 socket 連接,系統(tǒng)進程將 service 端的 socket 傳送到 InputDispatcher ,把 client 端的 socket 返回給 app 。當事件到達時,InputDispatcher 找到此時具有焦點的窗口,通過 socket 把事件發(fā)送該 socket 。

在此過程中,socket 會被封裝成 InputChannel :

設置 InputChannel 的流程:

可以看到,在添加窗口時,IMS 向 linux 系統(tǒng)請求創(chuàng)建一對 socket 并封裝成 InputChannel ,server 端的留在 InputDispatcher ,client 端的傳送給 app 端的 ViewRootImpl 。因此 events 可以直接從 InputDispatcher 分發(fā)給對應的 ViewRootImpl 。
簡述:

那剩下就是如何找到合適的窗口(ViewRootImp)派發(fā)事件?
答:InputDispatcher 保存了系統(tǒng)中每個窗口 layer 的信息,包括這個窗口是否可見、是否可以接收輸入事件、是否聚焦等。在事件派發(fā)時,只要找到當前聚焦的窗口的 InputChannel 并通過它發(fā)給目標窗口即可。而窗口的信息由 WMS 通過 InputMonitor 更新到 InputDispatcher 中。如圖:

其中聚焦的信息封裝在 FocusRequest 中:

其流程可概括為:

具體流程為:

總結,InputDispatcher 和 ViewRootImpl 之間的交互過程如圖:

具體流程為:

ViewRootImpl 事件派發(fā)
窗口是怎么把事件轉發(fā)給正確的 view 呢?
答:ViewRootImpl 封裝了一系列的 InputStage 來處理輸入事件,這些 InputStage 組成鏈式結構,如果上一個節(jié)點的 InputStage 沒處理則傳給下個節(jié)點處理。在 InputStage 中通過事件類型來區(qū)分怎么處理,一般來說 Listener 比 View 先處理。如,事件為 KeyEvent 時,OnKeyListener 先處理,再是 View 處理 onKeyDown、onKeyUp 等。
簡要概括流程為:

其中 InputStage 的處理順序為:

事件類型分為:

以 pointer 事件和 key 事件為例其具體流程如下:

總結
簡要概述一下 Android 事件的流程就是:
- Linux 內核中的輸入設備驅動程序將物理輸入設備產生的輸入信號轉換成標準 Linux 輸入事件格式;
- Android EventHub 組件打開與每個輸入設備關聯(lián)的 evdev 驅動程序,并從 Linux 內核中讀取已轉成標準 Linux 輸入事件格式的輸入信號,再通過 Android 的 InputReader 組件轉碼成 Android 事件輸入流并發(fā)送給 InputDispatcher。
- InputDispatcher 將事件通過 socket 發(fā)送給系統(tǒng)當前聚焦的窗口。
- 窗口將事件派發(fā)給需要處理這個事件的 view,如 Touch Event 派發(fā)給當前 window 中管轄觸摸點 [x,y] 的最小的可接收事件的 View ,而 Key Event 派發(fā)給當前窗口中聚焦的 View 。
參考
Android 官方:輸入
《深入理解 Android 內核設計思想》上,著:林學森
Android 官方代碼
原創(chuàng)文章,歡迎轉載,但請注明出處