Android用戶事件輸入路徑
1 輸入路徑的一般原理
按鍵,鼠標(biāo)消息從收集到最終將發(fā)送到焦點(diǎn)窗口,要經(jīng)歷怎樣的路徑,是Android GWES設(shè)計(jì)方案中需要詳細(xì)考慮的問題。按鍵,鼠標(biāo)等用戶消息消息的處理可分為不同的情況進(jìn)行判定:
(1)用戶輸入根據(jù)系統(tǒng)狀況是否應(yīng)該派送。如在ScreenOff的情況下,在按鍵屬于特殊按鍵的情況下等
(2)是否有攔截Listener
(3)對(duì)按鍵事件來講,是否存在輸入法
(4)是否是焦點(diǎn)終點(diǎn)
(5)是否為焦點(diǎn)切換按相關(guān)鍵
這些情況都是設(shè)計(jì)輸入路徑需要考慮的基本條件。
1.1一般的輸入路徑設(shè)計(jì)
該輸入路徑實(shí)際上是指的按鍵消息(MSG_KEYDOWN,MSG_KEYUP, MSG_LongPress)的輸入路徑,即從活動(dòng)主窗口到焦點(diǎn)窗口所經(jīng)歷的路程。 將信息輸入路徑分為兩步:
Step 1)窗口管理器將信息發(fā)送到活動(dòng)窗口
Step 2)活動(dòng)窗口通過缺省處理函數(shù)將該消息一層層的傳遞到焦點(diǎn)。
這樣應(yīng)用程序可以在活動(dòng)View的處理函數(shù)中來預(yù)先處理用戶輸入信息,從而增強(qiáng)應(yīng)用對(duì)用戶信息的控制力。
傳遞路徑是通過View的缺省處理函數(shù)Onxxx來完成。通過ActiveView ->focus->focus->focus的鏈條關(guān)系,一級(jí)一級(jí)的將按鍵消息MSG_KEYDOWN,MSG_KEYUP, MSG_CHAR等傳遞到focus窗口。
此時(shí)用戶按鍵輸入先發(fā)送到輸入法窗口,經(jīng)過輸入法管理器處理,過濾后將輸入法產(chǎn)生的結(jié)果放置到焦點(diǎn)View。
1.3輸入系統(tǒng)整體流程
下面示意圖是Android輸入系統(tǒng)的數(shù)據(jù)流途徑,通過WM的輸入系統(tǒng)線程收集消息,分發(fā)到Focus Activity消息隊(duì)列,然后通過消息系統(tǒng)派發(fā)。 2 Android輸入路徑詳細(xì)描述 2.1 第一步:用戶數(shù)據(jù)收集及其初步判定
KeyInputQ在WindowMangerService中建立一個(gè)獨(dú)立的線程InputDeviceReader,使用Native函數(shù)readEvent來讀取Linux Driver的數(shù)據(jù)構(gòu)建RawEvent,放入到KeyQ消息隊(duì)列中。 preProcessEvent()@KeyInptQ@KeyInputQueue.java這個(gè)是在輸入系統(tǒng)中的第一個(gè)攔截函數(shù)原型。KeyQ重載了preProcessEvent()@WindowManagerService.java。在該成員函數(shù)中進(jìn)行如下動(dòng)作:
(1) 根據(jù)PowerManager獲取的Screen on,Screen off狀態(tài)來判定用戶輸入的是否WakeUPScreen。
(2) 如果按鍵式應(yīng)用程序切換按鍵,則切換應(yīng)用程序。
(3) 根據(jù)WindowManagerPolicy覺得該用戶輸入是否投遞。
2.2 第二步 消息分發(fā)第一層面
InputDispatcherThread從KeyQ中讀取Events,找到Window Manager中的Focus Window,通過Focus Window記錄的mClient接口,將Events專遞到Client端。 如何將KeyEvent對(duì)象傳到Client端: 在前面的章節(jié)(窗口管理ViewRoot,Window Manager Proxy)我們已經(jīng)知道:在客戶端建立Window Manager Proxy后,添加窗口到Window Manager service時(shí),帶了一個(gè)跟客戶ViewRoot相關(guān)的IWindow接口實(shí)例過去,記錄在WindowState中的mClient成員變量中。通過IWindow這個(gè)AIDL接口實(shí)例,Service可以訪問客戶端的信息,IWindow是Service連接View橋梁。
IWindow:dispatchKey(event)
dispatchKey(event)@W@ViewRoot@ViewRoot.java
ViewRoot.dispatchKey(event)@ViewRoot.java
message>
sendMessageAtTime(msg)@Handler@Handler.java
至此我們通過前面的Looper,Handler詳解章節(jié)的分析結(jié)論,我們可以知道Key Message已經(jīng)放入到應(yīng)用程序的消息隊(duì)列。
2.3第三步:應(yīng)用消息隊(duì)列分發(fā)
消息的分發(fā),在Looper,Handler詳解章節(jié)我們分析了Looper.loop()在最后后面調(diào)用了handleMesage. …
ActivityThread.main()
Looper.loop()
ViewRoot$RootHandler().dispatch()
handleMessage
....
注意到在分發(fā)的調(diào)用msg.target.dispatch(),而這個(gè)target在第二層將消息sendMessageAtTime到消息隊(duì)列時(shí)填入了mag.target=this即為msg.target=ViewRoot實(shí)例。所有此時(shí)handleMessage就是ViewRoot重載的handleMessage函數(shù)。
deliverkeyEvent
如果輸入法存在,dispatchKey到輸入法服務(wù)。
在這里需要強(qiáng)調(diào)的是,輸入法的KeyEvent的攔截并沒有放入到Window Manager Service中,而是放入到了客戶端的RootView中來處理。
2.4第四步:向焦點(diǎn)進(jìn)發(fā),完成焦點(diǎn)路徑的遍歷。
mView.dispatchKeyEvent:mView是與ViewRoot相對(duì)應(yīng)的Top-Level View.如果mView是一個(gè)ViewGroup則分發(fā)消息到他的mFocus。
mView.dispatchKeyEvent @ViewGroup (ViewRoot@root)
Event.dispatch
mFocus.dispatchKeyEevnet
如果此時(shí)的mFocu還是一個(gè)ViewGroup,這回將事件專遞到下一層的焦點(diǎn),直到mFocus為一個(gè)View。通過這輪調(diào)用,就遍歷了焦點(diǎn)Path,至此,用戶事件傳遞完成一個(gè)段落。
2.5第五步 缺省處理
如果事件在上述Focus View沒有處理掉,并且為方向鍵之類的焦點(diǎn)轉(zhuǎn)換相關(guān)按鍵,則轉(zhuǎn)移焦點(diǎn)到下一個(gè)View。 |
|