Android使用者事件輸入路徑 1 輸入路徑的一般原理 按鍵,滑鼠訊息從收集到最終將發送到焦點視窗,要經曆怎樣的路徑,是Android GWES設計方案中需要詳細考慮的問題。按鍵,滑鼠等使用者訊息訊息的處理可分為不同的情況進行判定:(1)使用者輸入根據系統狀況是否應該派送。如在ScreenOff的情況下,在按鍵屬於特殊按鍵的情況下等(2)是否有攔截Listener(3)對按鍵事件來講,是否存在IME(4)是否是焦點終點(5)是否為焦點切換按相關鍵這些情況都是設計輸入路徑需要考慮的基本條件。1.1一般的輸入路徑設計該輸入路徑實際上是指的按鍵訊息(MSG_KEYDOWN,MSG_KEYUP, MSG_LongPress)的輸入路徑,即從活動主視窗到焦點視窗所經曆的路程。將資訊輸入路徑分為兩步:Step 1)視窗管理器將資訊發送到使用中視窗Step 2)使用中視窗通過預設處理函數將該訊息一層層的傳遞到焦點。這樣應用程式可以在活動View的處理函數中來預先處理使用者輸入資訊,從而增強應用對使用者資訊的控制力。傳遞路徑是通過View的預設處理函數Onxxx來完成。通過ActiveView ->focus->focus->focus的鏈條關係,一級一級的將按鍵訊息MSG_KEYDOWN,MSG_KEYUP, MSG_CHAR等傳遞到focus視窗。此時使用者按鍵輸入先發送到IME視窗,經過IME管理器處理,過濾後將IME產生的結果放置到焦點View。1.3輸入系統整體流程下面是Android輸入系統的資料流途徑,通過WM的輸入系統線程收集訊息,分發到Focus Activity訊息佇列,然後通過訊息系統派發。 2 Android輸入路徑詳細描述 2.1 第一步:使用者資料收集及其初步判定 KeyInputQ在WindowMangerService中建立一個獨立的線程InputDeviceReader,使用Native函數readEvent來讀取Linux Driver的資料構建RawEvent,放入到KeyQ訊息佇列中。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端。如何將KeyEvent對象傳到Client端:在前面的章節(視窗管理ViewRoot,Window Manager Proxy)我們已經知道:在用戶端建立Window Manager Proxy後,添加視窗到Window Manager service時,帶了一個跟客戶ViewRoot相關的IWindow介面執行個體過去,記錄在WindowState中的mClient成員變數中。通過IWindow這個AIDL介面執行個體,Service可以訪問用戶端的資訊,IWindow是Service串連View橋樑。看看在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 如果IME存在,dispatchKey到IME服務。 否則deliverKeyEventToViewHierarchy@ViewRoot.java 在這裡需要強調的是,IME的KeyEvent的攔截並沒有放入到Window Manager Service中,而是放入到了用戶端的RootView中來處理。2.4第四步:向焦點進發,完成焦點路徑的遍曆。分發函數調用棧deliverKeyEventToViewHierarchy@ViewRoot.javamView.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。