前言
本文代碼基于 Android 12 脊串。
概述
Android 的事件輸入可以簡化為三部分:
- 物理輸入設(shè)備 -> InputDispatcher
- InputDispatcher -> ViewRootImpl
- ViewRootImpl 事件派發(fā)
物理輸入設(shè)備 -> InputDispatcher
這一部分主要有三個流程:
- 物理輸入設(shè)備 -> 標準的 Linux 輸入事件
- 標準的 Linux 輸入事件 -> Android 輸入事件
- Android 輸入事件 -> Android 顯示器上的窗口
物理輸入設(shè)備 -> 標準的 Linux 輸入事件
這一部分主要由 Linux 內(nèi)核中的輸入設(shè)備驅(qū)動程序完成:
物理設(shè)備生成輸入事件信號后匆光,會由設(shè)備固件編碼成設(shè)備特有的事件信號撑教,并傳輸給 Linux 系統(tǒng)则吟,再由 Linux 內(nèi)核中的輸入設(shè)備驅(qū)動程序解碼成標準的 Linux 輸入事件格式垦垂。
標準的 Linux 輸入事件 -> Android 輸入事件
這一部分主要由 Android InputReader 實現(xiàn):
Android EventHub 組件會打開與每個輸入設(shè)備關(guān)聯(lián)的 evdev 驅(qū)動程序并從 Linux 內(nèi)核中讀取這些標準的輸入事件痹升,再由 Android InputReader 組件根據(jù)設(shè)備類別解碼成 Android 定義的事件阿迈。
Android 輸入事件 -> Android 顯示器上的窗口
該部分主要由 Android InputDispatcher 組件完成:
Android InputReader 會把生成的 Android 輸入事件流發(fā)送給 InputDispatcher 攻谁,然后由 InputDispatcher 將這些事件轉(zhuǎn)發(fā)給對應(yīng)的窗口稚伍。
如圖:
根據(jù)物理輸入設(shè)備的不同,Android 輸入事件主要分為 KeyEvent 和 MotionEvent 戚宦。
所以用來上傳 KeyEvent 的設(shè)備有物理鍵盤設(shè)備个曙、DPad 等。而上傳 MotionEvent 的設(shè)備有顯示屏設(shè)備受楼、鼠標垦搬、手寫筆等。
InputDispatcher -> ViewRootImpl
InputDispatcher 是怎么把事件轉(zhuǎn)發(fā)給對應(yīng)的窗口呢艳汽?
答:app 在向系統(tǒng)添加窗口時猴贰,會和系統(tǒng)進程建立 socket 連接,系統(tǒng)進程將 service 端的 socket 傳送到 InputDispatcher 河狐,把 client 端的 socket 返回給 app 米绕。當事件到達時,InputDispatcher 找到此時具有焦點的窗口馋艺,通過 socket 把事件發(fā)送該 socket 栅干。
在此過程中,socket 會被封裝成 InputChannel :
設(shè)置 InputChannel 的流程:
可以看到捐祠,在添加窗口時碱鳞,IMS 向 linux 系統(tǒng)請求創(chuàng)建一對 socket 并封裝成 InputChannel ,server 端的留在 InputDispatcher 雏赦,client 端的傳送給 app 端的 ViewRootImpl 劫笙。因此 events 可以直接從 InputDispatcher 分發(fā)給對應(yīng)的 ViewRootImpl 芙扎。
簡述:
那剩下就是如何找到合適的窗口(ViewRootImp)派發(fā)事件?
答:InputDispatcher 保存了系統(tǒng)中每個窗口 layer 的信息填大,包括這個窗口是否可見戒洼、是否可以接收輸入事件、是否聚焦等允华。在事件派發(fā)時圈浇,只要找到當前聚焦的窗口的 InputChannel 并通過它發(fā)給目標窗口即可。而窗口的信息由 WMS 通過 InputMonitor 更新到 InputDispatcher 中靴寂。如圖:
其中聚焦的信息封裝在 FocusRequest 中:
其流程可概括為:
具體流程為:
總結(jié)磷蜀,InputDispatcher 和 ViewRootImpl 之間的交互過程如圖:
具體流程為:
ViewRootImpl 事件派發(fā)
窗口是怎么把事件轉(zhuǎn)發(fā)給正確的 view 呢?
答:ViewRootImpl 封裝了一系列的 InputStage 來處理輸入事件百炬,這些 InputStage 組成鏈式結(jié)構(gòu)褐隆,如果上一個節(jié)點的 InputStage 沒處理則傳給下個節(jié)點處理。在 InputStage 中通過事件類型來區(qū)分怎么處理剖踊,一般來說 Listener 比 View 先處理庶弃。如,事件為 KeyEvent 時德澈,OnKeyListener 先處理歇攻,再是 View 處理 onKeyDown、onKeyUp 等梆造。
簡要概括流程為:
其中 InputStage 的處理順序為:
事件類型分為:
以 pointer 事件和 key 事件為例其具體流程如下:
總結(jié)
簡要概述一下 Android 事件的流程就是:
- Linux 內(nèi)核中的輸入設(shè)備驅(qū)動程序?qū)⑽锢磔斎朐O(shè)備產(chǎn)生的輸入信號轉(zhuǎn)換成標準 Linux 輸入事件格式缴守;
- Android EventHub 組件打開與每個輸入設(shè)備關(guān)聯(lián)的 evdev 驅(qū)動程序,并從 Linux 內(nèi)核中讀取已轉(zhuǎn)成標準 Linux 輸入事件格式的輸入信號镇辉,再通過 Android 的 InputReader 組件轉(zhuǎn)碼成 Android 事件輸入流并發(fā)送給 InputDispatcher屡穗。
- InputDispatcher 將事件通過 socket 發(fā)送給系統(tǒng)當前聚焦的窗口。
- 窗口將事件派發(fā)給需要處理這個事件的 view摊聋,如 Touch Event 派發(fā)給當前 window 中管轄觸摸點 [x,y] 的最小的可接收事件的 View 鸡捐,而 Key Event 派發(fā)給當前窗口中聚焦的 View 栈暇。
參考
Android 官方:輸入
《深入理解 Android 內(nèi)核設(shè)計思想》上麻裁,著:林學(xué)森
Android 官方代碼
原創(chuàng)文章,歡迎轉(zhuǎn)載源祈,但請注明出處