# Android用戶事件輸入路徑
### 1 輸入路徑的一般原理
按鍵,鼠標消息從收集到最終將發送到焦點窗口,要經歷怎樣的路徑,是Android GWES設計方案中需要詳細考慮的問題。按鍵,鼠標等用戶消息消息的處理可分為不同的情況進行判定:
(1)用戶輸入根據系統狀況是否應該派送。如在ScreenOff的情況下,在按鍵屬于特殊按鍵的情況下等
(2)是否有攔截Listener
(3)對按鍵事件來講,是否存在輸入法
(4)是否是焦點終點
(5)是否為焦點切換按相關鍵
這些情況都是設計輸入路徑需要考慮的基本條件。
### 1.1一般的輸入路徑設計
該輸入路徑實際上是指的按鍵消息(MSG_KEYDOWN,MSG_KEYUP, MSG_LongPress)的輸入路徑,即從活動主窗口到焦點窗口所經歷的路程。
[](http://hi.csdn.net/attachment/201005/5/0_1273072314A4ZR.gif)
將信息輸入路徑分為兩步:
Step 1)窗口管理器將信息發送到活動窗口
Step 2)活動窗口通過缺省處理函數將該消息一層層的傳遞到焦點。
這樣應用程序可以在活動View的處理函數中來預先處理用戶輸入信息,從而增強應用對用戶信息的控制力。
[](http://hi.csdn.net/attachment/201005/5/0_1273072315h9Yf.gif)
傳遞路徑是通過View的缺省處理函數Onxxx來完成。通過ActiveView ->focus->focus->focus的鏈條關系,一級一級的將按鍵消息MSG_KEYDOWN,MSG_KEYUP, MSG_CHAR等傳遞到focus窗口。
[](http://hi.csdn.net/attachment/201005/5/0_1273072316MABo.gif)
此時用戶按鍵輸入先發送到輸入法窗口,經過輸入法管理器處理,過濾后將輸入法產生的結果放置到焦點View。
### 1.3輸入系統整體流程
下面示意圖是Android輸入系統的數據流途徑,通過WM的輸入系統線程收集消息,分發到Focus Activity消息隊列,然后通過消息系統派發。
[](http://hi.csdn.net/attachment/201005/5/0_1273072319mmM9.gif)
### 2 Android輸入路徑詳細描述
### 2.1 第一步:用戶數據收集及其初步判定
KeyInputQ在WindowMangerService中建立一個獨立的線程InputDeviceReader,使用Native函數readEvent來讀取Linux Driver的數據構建RawEvent,放入到KeyQ消息隊列中。
[](http://hi.csdn.net/attachment/201005/5/0_1273072322wE2w.gif)
preProcessEvent()@KeyInptQ@KeyInputQueue.java這個是在輸入系統中的第一個攔截函數原型。KeyQ重載了preProcessEvent()@WindowManagerService.java。在該成員函數中進行如下動作:
(1) 根據PowerManager獲取的Screen on,Screen off狀態來判定用戶輸入的是否WakeUPScreen。
(2) 如果按鍵式應用程序切換按鍵,則切換應用程序。
(3) 根據WindowManagerPolicy覺得該用戶輸入是否投遞。
### 2.2 第二步 消息分發第一層面
InputDispatcherThread從KeyQ中讀取Events,找到Window Manager中的Focus Window,通過Focus Window記錄的mClient接口,將Events專遞到Client端。
[](http://hi.csdn.net/attachment/201005/5/0_12730723237679.gif)
如何將KeyEvent對象傳到Client端:
在前面的章節(窗口管理ViewRoot,Window Manager Proxy)我們已經知道:在客戶端建立Window Manager Proxy后,添加窗口到Window Manager service時,帶了一個跟客戶ViewRoot相關的IWindow接口實例過去,記錄在WindowState中的mClient成員變量中。通過IWindow這個AIDL接口實例,Service可以訪問客戶端的信息,IWindow是Service連接View橋梁。
[
](http://hi.csdn.net/attachment/201005/5/0_1273072324aNrZ.gif)
看看在Client ViewRootKeyEvent的分發過程
IWindow:dispatchKey(event)
dispatchKey(event)@W@ViewRoot@ViewRoot.java
ViewRoot.dispatchKey(event)@ViewRoot.java
<message>
sendMessageAtTime(msg)@Handler@Handler.java
至此我們通過前面的Looper,Handler詳解章節的分析結論,我們可以知道Key Message已經放入到應用程序的消息隊列。
### 2.3第三步:應用消息隊列分發
消息的分發,在Looper,Handler詳解章節我們分析了Looper.loop()在最后后面調用了handleMesage.
…
ActivityThread.main()
Looper.loop()
ViewRoot$RootHandler().dispatch()
handleMessage
....
注意到在分發的調用msg.target.dispatch(),而這個target在第二層將消息sendMessageAtTime到消息隊列時填入了mag.target=this即為msg.target=ViewRoot實例。所有此時handleMessage就是ViewRoot重載的handleMessage函數。
handlerMessage@ViewRoot@ViewRoot.java
deliverkeyEvent
如果輸入法存在,dispatchKey到輸入法服務。
否則[deliverKeyEventToViewHierarchy@ViewRoot.java](#)
在這里需要強調的是,輸入法的KeyEvent的攔截并沒有放入到Window Manager Service中,而是放入到了客戶端的RootView中來處理。
### 2.4第四步:向焦點進發,完成焦點路徑的遍歷。
[](#)[
](http://hi.csdn.net/attachment/201005/5/0_12730723266xTM.gif)
分發函數調用棧
[deliverKeyEventToViewHierarchy@ViewRoot.java](#)
mView.dispatchKeyEvent:mView是與ViewRoot相對應的Top-Level View.如果mView是一個ViewGroup則分發消息到他的mFocus。
mView.dispatchKeyEvent @ViewGroup? (ViewRoot@root)
Event.dispatch
mFocus.dispatchKeyEevnet
如果此時的mFocu還是一個ViewGroup,這回將事件專遞到下一層的焦點,直到mFocus為一個View。通過這輪調用,就遍歷了焦點Path,至此,用戶事件傳遞完成一個段落。
### 2.5第五步 缺省處理
如果事件在上述Focus View沒有處理掉,并且為方向鍵之類的焦點轉換相關按鍵,則轉移焦點到下一個View。
- 前言
- (一)分析方法論探討之設計意圖
- (二)方法論探討之概念空間篇
- (三)手機之硬件形態
- (四)手機的軟件形態
- (五)基本空間劃分
- (六)IPC框架分析 Binder,Service,Service manager
- (七)Service深入分析
- (八)Android 啟動過程詳解
- (九)Zygote Service
- (十)Android GWES之基本原理篇
- (十一)Android GWES之消息系統
- (十二)Android GEWS窗口管理之基本架構原理
- (十三)Android GWES之Android窗口管理
- (十四)Android GWES之輸入系統
- (十五)Android輸入系統之輸入路徑詳解
- (十六)Android電話系統-概述篇
- (十七)電話系統之rilD
- (十八)Android電話系統之RIL-Java
- (十九)電話系統之GSMCallTacker
- (二十)Android應用程序框架之無邊界設計意圖
- (二十一)Android應用框架之AndroidApplication
- (二十二)Android應用框架之Activity
- (二十三)Andoird GDI之基本原理及其總體框架
- (二十四)Android GDI之顯示緩沖管理
- (二十五)Android GDI之共享緩沖區機制
- (二十六)Android GDI之SurfaceFlinger
- (二十七)Android GDI 之SurfaceFlinger之動態結構示意圖
- (二十八)Android GDI之Surface&amp;Canvas