Android輸入系統(tǒng)——InputManagerService

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



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盔粹,隨后出現(xiàn)的幾起案子堂竟,更是在濱河造成了極大的恐慌,老刑警劉巖玻佩,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件出嘹,死亡現(xiàn)場離奇詭異,居然都是意外死亡咬崔,警方通過查閱死者的電腦和手機税稼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垮斯,“玉大人郎仆,你說我怎么就攤上這事《等洌” “怎么了扰肌?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長熊杨。 經(jīng)常有香客問我曙旭,道長,這世上最難降的妖魔是什么晶府? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任桂躏,我火速辦了婚禮,結(jié)果婚禮上川陆,老公的妹妹穿的比我還像新娘剂习。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布鳞绕。 她就那樣靜靜地躺著失仁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪们何。 梳的紋絲不亂的頭發(fā)上陶因,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音,去河邊找鬼解幽。 笑死贴见,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的躲株。 我是一名探鬼主播片部,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼霜定!你這毒婦竟也來了档悠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤望浩,失蹤者是張志新(化名)和其女友劉穎辖所,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磨德,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡缘回,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了典挑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酥宴。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖您觉,靈堂內(nèi)的尸體忽然破棺而出拙寡,到底是詐尸還是另有隱情,我是刑警寧澤琳水,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布肆糕,位于F島的核電站,受9級特大地震影響在孝,放射性物質(zhì)發(fā)生泄漏擎宝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一浑玛、第九天 我趴在偏房一處隱蔽的房頂上張望绍申。 院中可真熱鬧,春花似錦、人聲如沸极阅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筋搏。三九已至仆百,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奔脐,已是汗流浹背俄周。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留髓迎,地道東北人峦朗。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像排龄,于是被迫代替她去往敵國和親波势。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容