本博客在 https://wkmcyz.notion.site/InputManagerService-616004c0a3fa4324b952f7e8563a010f 可以獲得更好的閱讀體驗。
Android 的觸摸事件,可以分為三部分。
第一部分是硬件部分,檢測觸摸事件,這一步需要傳感器之類的組件來完成。這部分主要是硬件相關(guān)的知識。
第二部分是操作系統(tǒng)部分, 將觸摸事件傳遞給應(yīng)該接受它的目標(biāo)應(yīng)用。本篇后面會說的 InputManagerService 的相關(guān)內(nèi)容就是解釋這部分的流程的。
第三部分是控件級別(即各個 View 類)的處理,來消化該觸摸事件。這是 Android 應(yīng)用的 view 觸摸事件處理,不在此討論
InputManagerService 是 Android 的一個系統(tǒng)服務(wù),運行在 system_server 進程中,和 WMS 一起配合維護著 Android 的輸入事件的管理,也就是上面提到的 “操作系統(tǒng)處理” 的部分。
想象一下,這樣一個傳遞觸摸事件的管理者需要進行哪些操作。主要應(yīng)該分為三步,第一是從硬件部分接收事件;第二是進行一些自己的處理;第三是將這部分事件傳遞給其對應(yīng)的進程以及對應(yīng)的控件。我們也主要對這三步進行說明。
第一步:從設(shè)備讀取原始的輸入事件
Android 設(shè)備在發(fā)生輸入事件的時候,會將觸摸事件寫入一個文件,InputManagerService 依賴 epoll 機制從該文件讀取觸摸事件的信息,讀取到的便是原始的輸入事件。這就是最初 InputManagerService 的輸入。
第二步:IMS 對輸入事件進行處理
IMS 會執(zhí)行一個中間者的職責(zé),將原始輸入信息轉(zhuǎn)化成應(yīng)用希望得到的方便好用的信息——也就是 Android 系統(tǒng)提供的輸入事件類型。上面提到 IMS 接受設(shè)備給出的原始輸入信息,其中設(shè)備會有很多不同的種類,例如外接的鍵盤、外接的鼠標(biāo)、手柄以及觸摸屏幕等。
這些不同的輸入設(shè)備會將不同的操作映射為自己獨特的事件 id,例如按下鍵盤的某個按鍵會被記錄為一次 id 為 1 的事件,或者鼠標(biāo)左鍵回應(yīng)設(shè)為 id 為 1 的事件等。IMS 接收到以后,該怎么樣不同的 id 映射為指定的事件呢?這是根據(jù)輸入設(shè)備的類型來決定的。所以這里 IMS 會先根據(jù)輸入設(shè)備的類型獲得一個對應(yīng)表,也就是一個 Map。其負責(zé)將將讀入的原始輸入信息進行映射處理,這可以認為是一次“解碼”。
第三步:IMS 進行輸入事件的分發(fā)
第二步的處理會將原始輸入事件轉(zhuǎn)變成 Android 框架提供的事件類型進行分發(fā),這些事件類型包括 1) Key 事件 ,2)Motion 事件。分發(fā)的過程涉及到了當(dāng)前輸入事件的接受者的尋找,因此這部分是需要和 WMS 互相配合以實現(xiàn)該判斷的。
WMS 會在管理的窗口布局發(fā)生變化的時候,將新的窗口信息都通知給 IMS,這包括每個窗口的位置、寬高、zOrder 、窗口的 flag 等信息,還有焦點窗口等信息,以供 IMS 為輸入事件選擇目標(biāo)。
IMS 的分發(fā)相關(guān)的處理,簡單描述如下:
其中分發(fā)步驟會有很多處理,會根據(jù) IMS 服務(wù)所處的狀態(tài)決定處理邏輯,這包括在系統(tǒng)休眠的時候或者設(shè)備正在旋轉(zhuǎn)的時候,這時候的輸入事件會被緩存下來或者丟棄掉。
IMS 會根據(jù)當(dāng)前系統(tǒng)的窗口布局情況以及輸入事件的屬性選擇適合的窗口來處理輸入事件。比如有一些特定屬性的輸入事件不會交給用戶進程處理;也可能會因為一些原因?qū)⑤斎胧录G棄。
IMS 在為輸入事件選擇好目標(biāo)對象——也就是接受輸入事件的窗口——的時候,會判斷目標(biāo)是否可以接收輸入事件,如果可以的話,將事件發(fā)送給目標(biāo)。如果目標(biāo)無法接收的話,會先將輸入事件緩存起來,在其可以接受的時候重新發(fā)送,或者是在 5s 之后上報 ANR。
相關(guān)類的對應(yīng)關(guān)系
IMS 從操作系統(tǒng)讀取原始輸入事件 :
IMS 將原始輸入事件轉(zhuǎn)成 Android 系統(tǒng)的輸入事件 :
IMS 將輸入事件分派給指定窗口 :
IMS 將輸入事件傳輸給指定窗口 :
窗口接收輸入事件: