InputManagerService(IMS)
Linux內(nèi)核:接受輸入設(shè)備的中斷疼进,并將原始事件的數(shù)據(jù)寫入設(shè)備節(jié)點中設(shè)備接電合陵,作為內(nèi)核與 IMS 的橋梁厨埋,將原始事? ?件的數(shù)據(jù)暴露給用戶空間抖拦,以便 IMS 可以從中讀取事件雀鹃;
InputManagerService:一個 Android 系統(tǒng)服務(wù)久又,分為 Java 層和 Native 層兩部分巫延,Java 層負責(zé)與 WMS 通信,而 Native 層則是 InputReader 和 InputDispatcher 兩個輸入系統(tǒng)關(guān)鍵組件的運行容器地消;
EventHub:直接訪問所有的設(shè)備節(jié)點炉峰,通過一個名為 getEvents() 的函數(shù)將所有輸入系統(tǒng)相關(guān)的待處理的底層事件返回給使用者,包括原始輸入事件犯建,設(shè)備節(jié)點的增刪等讲冠;
InputReader:IMS 中的關(guān)鍵組件之一,它運行在一個獨立的線程中适瓦,負責(zé)管理輸入設(shè)備的列表和配置竿开,以及進行輸入事件的加工處理,它通過其線程循環(huán)不斷地通過 getEvents() 函數(shù)從 EventHub 中將事件取出并進行處理玻熙,對于設(shè)備節(jié)點的增刪事件否彩,它會更新輸入設(shè)備列表與配置;對于原始輸入事件嗦随,InputReader對其進行翻譯列荔,組裝,封裝為包含更多信息枚尼,更多可讀性的輸入事件贴浙,然后交給InputDispatcher進行派發(fā);
InputReaderPolicy:為 InputReader 的事件加工處理提供一些策略配置
InputDispatcher:是 IMS 中的另一個關(guān)鍵組件署恍,運行于一個獨立的線程中崎溃,InputDispatcher 中保管來自 WMS 的所有窗口的信息,收到 InputReader 的輸入事件后盯质,會在其保管的窗口中尋找合適的窗口袁串,并將事件派發(fā)給此窗口概而;
InputDispatcherPolicy:為 InputDispatcher 的派發(fā)過程提供策略控制,例如 HOME 鍵被 InputDispatcherPolicy 截取到 PhoneWindowManager 中處理囱修,并阻止窗口收到 HOME 鍵按下的事件赎瑰;
WindowManagerService:它并不是輸入系統(tǒng)的一員,新建窗口時破镰,WMS 為新窗口和 IMS 創(chuàng)建了事件傳遞所用的通道餐曼,會將窗口的可點擊區(qū)域,焦點窗口等信息實時更新到 IMS 的 InputDispatcher 中啤咽,使得 InputDispatcher 可以正確將事件派發(fā)到指定窗口晋辆;
ViewRootImpl:對某些窗口,如壁紙窗口宇整,SurfaceView 的窗口來說瓶佳,窗口就是輸入事件派發(fā)的終點,而對其他的如Activity鳞青,對話框等使用了 Android 控件系統(tǒng)的窗口來說霸饲,輸入事件的終點是控件;
IMS 的啟動流程
1. 創(chuàng)建新的IMS對象
SystemServer->ServerThread.run()
—> com_android_server_input_InputManagerService.cpp —> nativeInit()
創(chuàng)建了 NativeInputManager 對象臂拓,該對象實現(xiàn)了 InputReaderPolicyInterface 與 InputDispatcherPolicyInterface 接口厚脉,創(chuàng)建 EventHub 和 InputManager,InputManager 創(chuàng)建了 InputReader 與 InputDispatcher 以及 InputReaderThread 與 InputDispatcherThread
2.調(diào)用IMS對象的start函數(shù)完成啟動
InputReader的總流程
InputReader.cpp —> InputReaderThread::threadLoop()
分三步:
1.首先從 EventHub 抽取未處理的時間列表胶惰,一類是從設(shè)備節(jié)點讀取的原始輸入事件傻工,另一類則是輸入設(shè)備可用性變化事件,簡稱設(shè)備事件孵滞;
2.processEventsLocked 對事件進行處理中捆,對于設(shè)備事件,此函數(shù)對根據(jù)設(shè)備的可用性加載或移除設(shè)備對應(yīng)的配置信息坊饶,對原始輸入事件泄伪,則在進行轉(zhuǎn)譯,封裝與加工后將結(jié)果暫存到 mQueuedListener匿级;
3.所有事件處理完畢后蟋滴,調(diào)用 mQueuedListener.flush 將所有暫存的輸入事件一次性地交給InputDispatcher。
EventHub
1.設(shè)備節(jié)點監(jiān)聽的建立
通過 INotify 與 Epoll 機制建立起對設(shè)備節(jié)點增刪事件以及可讀狀態(tài)的監(jiān)聽
EventHub.cpp:EventHub
2.getEvents
使用 Epoll 的核心是 mPendingEventItems 數(shù)組痘绎,它是一個事件池津函,getEvents 包含了原始輸入事件讀取,輸入設(shè)備加載/卸載等操作孤页。
3.輸入設(shè)備管理
每個輸入設(shè)備在 dev/input/ 下有一個設(shè)備節(jié)點尔苦,設(shè)備節(jié)點包含輸入設(shè)備的所有信息,EventHub 負責(zé)在設(shè)備節(jié)點可用時加載并維護這些信息,并在設(shè)備節(jié)點被刪除時將其移除蕉堰,名為 Device 的私有結(jié)構(gòu)體保存。
4.原始輸入事件的監(jiān)聽與讀取
當設(shè)備的原始輸入事件到來之時悲龟,getevents 函數(shù)將會獲得一個 Epoll 事件屋讶,然后根據(jù) Epoll 事件讀取文件描述符的原始輸入事件,將其填充到 RawEvents 結(jié)構(gòu)體并放入 buffer 中被調(diào)用者取走须教。
InputReader
1.原始輸入事件的加工
InputReader:processEventsLocked
—> inputReader:processEventsForDeviceLocked
—>?InputReader:InputDevice::process
InputDevice 描述一個輸入設(shè)備皿渗,是一個存儲輸入設(shè)備信息的類
InputMapper 是 InputReader 中實際進行原始輸入事件加工的場所
2.InputDevice 與 InputMapper
inputDevice 創(chuàng)建 InputReader:addDeviceLocked
—>?InputReader:createDeviceLocked
3.keyboard 類型事件的加工處理
(1)keyboardInputMapper 配置(屏幕旋轉(zhuǎn)狀態(tài))
(2)鍵盤掃描碼與虛擬鍵值
掃描碼是硬件實現(xiàn),虛擬鍵值是操作系統(tǒng)實現(xiàn)
(3)掃描碼到虛擬鍵值的映射
InputReader —> KeyboardInputMapper:process
(4)按鍵事件的加工處理
InputReader?—> keyboardInputMapper:processKey
4.Touch類型事件的加工處理
(1)Touch 類型事件的信息與原始事件的組織方式
(2)TouchInputMapper 的體系
(3)MultiTouchInputMapper 的配置
MultiTouchInputMapper 的 configureRawPointerAxes 獲取來自設(shè)備節(jié)點的各項觸控信息轻腺,同時構(gòu)建傳感器的物理坐標系
TouchInputMapper 的 configureSurface 獲取來自 DisplayViewPort 的屏幕方向以及屏幕坐標系的信息乐疆,并計算物理坐標系到屏幕坐標系的差異信息
(4)點擊事件的信息收集
InputReader->MultiTouchInputMapper:process
->MultiTouchMotionAccumulator:process
(5)點擊事件信息的整合,變換與高級事件的生成
InputDispatcher——輸入事件的派發(fā)
1.將事件注入派發(fā)隊列
InputDispatcher 實現(xiàn)了 InputListenerInterface贬养,并在 InputReader 循環(huán)的最后挤土,QueuedInputListener 調(diào)用此接口將InputReader 產(chǎn)生的事件以及 NotifyXXXArgs 結(jié)構(gòu)體的形式提交給 InputDispatcher
—>?InputDispatcher:notifyMotionLocked
—>?InputDispatcher:enqueueInboundEventLocked
2.派發(fā)線程的線程循環(huán)
InputDispatcher:dispatchOnce
派發(fā)線程的一次循環(huán)包括以下三項:
進行一次事件派發(fā),事件的派發(fā)工作僅當命令隊列中沒有命令時才會進行误算,派發(fā)工作會設(shè)置nextWakeupTime指明隨后休眠時間長短
執(zhí)行命令列表中的命令
陷入休眠狀態(tài)
3.派發(fā)工作的整體流程
dispatchOnceInnerLocked函數(shù)體現(xiàn)派發(fā)過程
InputDispatcher:dispatchOnceInnerLocked
4.事件被丟棄的原因
.....
5.Motion事件目標窗口的確定
InputDispatcher:dispatchMotionLocked
三項工作:
對于被丟棄的事件仰美,返回 true
為事件尋找合適的窗口,窗口分為普通窗口和監(jiān)聽窗口儿礼,普通通過按點和焦點查找咖杂,監(jiān)聽窗口則無條件監(jiān)聽所有輸入事件
如果成功地找到可以接收事件的目標窗口,通過 dispatchEventLocked 完成實際的派發(fā)工作
6.向窗口發(fā)送事件
InputDispatcher:dispatchEventLocked
按鍵事件的派發(fā)
1.將事件注入派發(fā)隊列
2.額外的派發(fā)策略查詢
InputDispatcher:dispatchOnceInnerLocked
3.重復(fù)按鍵事件
InputDispatcher:dispatchOnceInnerLocked
—> InputDispatcher:dispatchKeyLocked 開啟與關(guān)閉重復(fù)按鍵模擬
—>?InputDispatcher:synthesizeKeyRepeatLocked 重復(fù)按鍵的生成
4.按鍵事件派發(fā)總結(jié)
按鍵事件通過 notifyKey 函數(shù)進入 InputDispatcher蚊夫,在注入派發(fā)隊列前诉字,使用 DispatcherPolicy 的interceptKeyBeforeQueueing 函數(shù)詢問后續(xù)的派發(fā)策略 policyFlag
按鍵事件在正式派發(fā)給窗口前,進行一次額外的派發(fā)策略查詢知纷,查詢的結(jié)果保存在 keyEntry:interceptKeyResult壤圃,結(jié)果覺得事件是正常派發(fā),稍后派發(fā)還是丟棄
當按鍵按下到按鍵抬起之間的時間里屈扎,dispatchOnceInnerLocked 和 dispatchKeyLocked 會協(xié)同工作完成對重復(fù)按鍵事件的模擬
按鍵事件的派發(fā)目標僅通過焦點方式進行查找
輸入事件的發(fā)送埃唯,接收和反饋
InputDispatcher 運行于 system_server 進程,窗口運行于其它的應(yīng)用進程中
InputChannel
InputChannel 的本質(zhì)是一對 SocketPair鹰晨,SocketPair 用來實現(xiàn)在本機內(nèi)進行進程間的通信
InputTransport —> InputChannel:openInputChannelPair
連接InputDispatcher和窗口
WinodwManagerService:addwindow
WMS 添加窗口時墨叛,會創(chuàng)建一對 InputChannel,其中一個保存在 WindowState 中模蜡,并注冊給 IMS漠趁,它是服務(wù)端,另一個則通過傳出參數(shù) outInputChannel 交給調(diào)用者忍疾,是客戶端
1.服務(wù)端連接的建立
addwindow 函數(shù)中闯传,有以下三項工作:
通過 WindowState.setInputChannel 函數(shù)保存服務(wù)端的 InputChannel
通過 IMS.registerInputChannel 將 InputChannel 注冊到 IMS
通過 InputMonitor.updateInputWindowsLw 將所有窗口的信息更新到 IMS
2.窗口端連接的建立
當窗口端通過 addwindow 函數(shù)獲取 InputChannel,便會使用它創(chuàng)建一個 InputEventReceiver 對象卤妒,可以接收來自InputChannel 的輸入事件甥绿,觸發(fā) onInputEvent 回調(diào)
InputEventRecevier 如何工作字币?將 InputChannel 的可讀事件注冊到 Looper,然后在事件到來時從 InputChannel 中讀取 InputMessage共缕,并翻譯成 InputEvent洗出,然后回調(diào) InputEventReceiver 的 onInputEvent
3.InputDispatcher與窗口的連接
事件的發(fā)送
派發(fā)循環(huán)是指 InputDispatcher 不斷地派發(fā)隊列取出事件,尋找合適的窗口并進行發(fā)送的過程图谷,是 InputDispatcher 線程的主要工作
事件發(fā)送循環(huán)是 InputDispatcher 通過 Connection 對象將事件發(fā)送給窗口翩活,并接受其反饋的過程
InputDispatcher —> dispatchEventLocked:dispatchEventLocked 根據(jù) InputTarget 中的 InputChannel 找到對應(yīng)的Connection
—>?InputDispatcher:prepareDispatcCycleLocked
—>?InputDispatcher:enqueueDispatchEntriesLocked
—>?InputDispatcher:startDispatchCycleLocked
輸入事件被 InputPublisher 以 InputMessage 的形式寫入 InputChannel,然后將事件轉(zhuǎn)存到 waitQueue 中等待窗口的反饋
事件的接收
當 InputPublisher 將事件以 InputMessage 的形式寫入 InputChannel 中便贵,窗口端的 Looper 會因此而被喚醒菠镇,并執(zhí)行NativeInputEventReceiver 的 handleEvent 調(diào)用 consumeEvent
—> android_view_InputEventReceiver —>?NativeInputEventReceiver:consumeEvent 讀取一個 InputEvent,生成 java 層的 InputEvent 對象承璃,最后通過 JNI 回調(diào)
—>?InputEventReceier:dispatchInputEvent
事件的反饋與發(fā)送
—>?inputEventReceiver:finishInputEvent
—>?android_view_InputEventReceiver —> NativeInputEventReceiver:finishInputEvent 觸發(fā)服務(wù)端 InputChannel 回調(diào)
—>?InputDispatcher:handleReceiveCallback
—>?InputDispatcher:doDispatchCycleFinishedLockedInterruptible
對于輸入事件反饋的處理主要有兩個方面
將事件從 Connection 的 waitQueue 隊列中刪除利耍,這個刪除動作標志著此事件的派發(fā)流程完成
最后調(diào)用 startDispatchCycleLocked 繼續(xù)嘗試發(fā)送隊列中的下一個事件
輸入事件ANR的產(chǎn)生
—>?InputDispatcher:findFocusedWindowTargetsLocked
1.窗口可以接收事件的條件
—>?InputDispatcher:isWindowReadyForMoreInputLocked 判斷窗口是否可以接收事件:InputPublisher 是否被阻塞以及 Connection 兩個隊列的狀態(tài)
2.重試派發(fā)與ANR的引發(fā)
—>?InputDispatcher:handleTargetsNotReadyLocked