Android input events 輸入系統(tǒng)

前言

本文代碼基于 Android 12 。

概述

Android 的事件輸入可以簡化為三部分:

  1. 物理輸入設備 -> InputDispatcher
  2. InputDispatcher -> ViewRootImpl
  3. ViewRootImpl 事件派發(fā)

物理輸入設備 -> InputDispatcher

這一部分主要有三個流程:

  1. 物理輸入設備 -> 標準的 Linux 輸入事件
  2. 標準的 Linux 輸入事件 -> Android 輸入事件
  3. 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 。


MotionEvent && KeyEvent

所以用來上傳 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 。

如圖:
InputDispatcher -> ViewRootImpl

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


InputChannel

設置 InputChannel 的流程:


設置 InputChannel

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

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


WMS -> InputMonitor -> InputDispatcher 設置 InputWindowHandle

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

其流程可概括為:
設置 focused app 和 window 流程

具體流程為:
設置 focused window

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


InputDispatcher ViewRootImpl 事件交互

具體流程為:


InputDispatcher 事件派發(fā)

ViewRootImpl 事件派發(fā)

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


app 分配 input event 流程

其中 InputStage 的處理順序為:


ViewRootImp InputStage 處理順序

事件類型分為:
事件類型

以 pointer 事件和 key 事件為例其具體流程如下:
ViewRootImpl 派發(fā)事件流程

總結

簡要概述一下 Android 事件的流程就是:

  1. Linux 內核中的輸入設備驅動程序將物理輸入設備產生的輸入信號轉換成標準 Linux 輸入事件格式;
  2. Android EventHub 組件打開與每個輸入設備關聯(lián)的 evdev 驅動程序,并從 Linux 內核中讀取已轉成標準 Linux 輸入事件格式的輸入信號,再通過 Android 的 InputReader 組件轉碼成 Android 事件輸入流并發(fā)送給 InputDispatcher。
  3. InputDispatcher 將事件通過 socket 發(fā)送給系統(tǒng)當前聚焦的窗口。
  4. 窗口將事件派發(fā)給需要處理這個事件的 view,如 Touch Event 派發(fā)給當前 window 中管轄觸摸點 [x,y] 的最小的可接收事件的 View ,而 Key Event 派發(fā)給當前窗口中聚焦的 View 。

參考

Android 官方:輸入
《深入理解 Android 內核設計思想》上,著:林學森
Android 官方代碼

原創(chuàng)文章,歡迎轉載,但請注明出處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容