在android系統(tǒng)中,鍵盤(pán)按鍵事件是由SystemServer服務(wù)來(lái)管理的;然后在以消息的形式分發(fā)給應(yīng)用程序處理。產(chǎn)生鍵盤(pán)按鍵事件則是有Linuxkernel的相關(guān)驅(qū)動(dòng)來(lái)實(shí)現(xiàn)。
鍵盤(pán)消息有別于其他類(lèi)型的消息;需要從Linuxkerneldrivers產(chǎn)生由上層app來(lái)處理。同時(shí)按鍵有著不同的映射值,因此從模塊獨(dú)立性角度各個(gè)獨(dú)立的模塊應(yīng)該擁有不同的鍵盤(pán)映射。這樣以來(lái),kernel產(chǎn)生的按鍵事件必然回經(jīng)過(guò)不同的映射才到app。
Android 使用標(biāo)準(zhǔn)的 linux 輸入事件設(shè)備(/dev/input/)和驅(qū)動(dòng)按鍵定義在 linux 內(nèi)核include/linux/input.h 中,按鍵的定義形式如下(僅以BACKHOME MENU為例):
有了按鍵的定義,就需要產(chǎn)生相應(yīng)的按鍵事件了。在kernel/arch/arm/mach-msm/xxx/xxx/xxx.c會(huì)對(duì)BACK HOME和MENU進(jìn)行注冊(cè)。這里使用在屏幕上的坐標(biāo)來(lái)對(duì)按鍵進(jìn)行區(qū)分。這部分代碼會(huì)在系統(tǒng)啟動(dòng)的時(shí)候,將相應(yīng)的數(shù)據(jù)存儲(chǔ),以供framework查詢(xún)。 (這里以xxx代替,是因?yàn)獒槍?duì)不同的硬件,需要的Linux kernel不同)
當(dāng)然從核心板原理圖到kernel是屬于驅(qū)動(dòng)范疇,不討論。 2、framework針對(duì)鍵盤(pán)事件的處理 上層對(duì)輸入事件的偵聽(tīng)和分發(fā)是在InputManagerService 中實(shí)現(xiàn) 首先來(lái)看看InputManagerService的創(chuàng)建, Step1 在SystemServer.java 點(diǎn)擊(此處)折疊或打開(kāi)
可以看到,在系統(tǒng)啟動(dòng)的時(shí)候,會(huì)首先創(chuàng)建一個(gè)系統(tǒng)級(jí)別的Handler線程wmHandlerThread用于處理鍵盤(pán)消息(僅說(shuō)明鍵盤(pán)消息)。然后在創(chuàng)建輸入管理服務(wù) inputManager,InputManagerService 的第二個(gè)參數(shù)就是用于處理按鍵消息的Handler。
Step2 在往下走到 InputManagerService.java的構(gòu)造函數(shù)。 點(diǎn)擊(此處)折疊或打開(kāi)
這里做了重要的兩件事情,第一:將SystemServer級(jí)別的Handler賦值給 InputManagerService自己的消息處理Handler;第二:調(diào)用nativeInit繼續(xù)進(jìn)行初始化。
Step3 com_android_server_InputManagerService.cpp 點(diǎn)擊(此處)折疊或打開(kāi)
這里nativeInit直接調(diào)用了 NativeInputManager的構(gòu)造函數(shù)
Step4 點(diǎn)擊(此處)折疊或打開(kāi)
這里需要特別注意最后兩行代碼。第一:創(chuàng)建了 EventHub;第二:創(chuàng)建 InputManager并將 EventHub作為參數(shù)傳入InputManager。
Step5 接下來(lái)繼續(xù)看看InputManager的構(gòu)造函數(shù)。 點(diǎn)擊(此處)折疊或打開(kāi)
創(chuàng)建了InputDispatcher 和InputReader,并調(diào)用了initialize函數(shù)創(chuàng)建了InputReaderThread和InputDispatcherThread。InputDispatcher類(lèi)是負(fù)責(zé)把鍵盤(pán)消息分發(fā)給當(dāng)前激活的Activity窗口的,而InputReader類(lèi)則是通過(guò) EventHub類(lèi)來(lái)實(shí)現(xiàn)讀取鍵盤(pán)事件的,InputReader實(shí)列mReader就是通過(guò)這里的 InputReaderThread線程實(shí)列mReaderThread來(lái)讀取鍵盤(pán)事件的,而InputDispatcher實(shí)例mDispatcher 則是通過(guò)這里的InputDispatcherThread線程實(shí)例mDisptacherThread來(lái)分發(fā)鍵盤(pán)消息的。 到這里,相關(guān)的組件都已經(jīng)被創(chuàng)建了;
Step6 接下來(lái)看看他們是如何運(yùn)行起來(lái)的。 在systemServer.java中創(chuàng)建inputManager之后。將InputManagerServer進(jìn)行注冊(cè),并運(yùn)行start() 點(diǎn)擊(此處)折疊或打開(kāi)
調(diào)用nativeStart繼續(xù)往下走。順帶說(shuō)一下,這里的參數(shù)mPtr是指向nativeinputmanager service對(duì)象的,在InputManagerService構(gòu)造函數(shù)中由nativeInit賦值。
Step7 接下來(lái)又到了com_android_server_InputManagerService.cpp中。 點(diǎn)擊(此處)折疊或打開(kāi)
這里的im就是inputManager并且用到了上面?zhèn)飨聛?lái)的mPtr來(lái)重新構(gòu)建。
Step8 繼續(xù)往下則會(huì)調(diào)用到InputManager.cpp 的start函數(shù) 點(diǎn)擊(此處)折疊或打開(kāi)
這個(gè)函數(shù)主要就是分別啟動(dòng)一個(gè)InputDispatcherThread線程和一個(gè)InputReaderThread線程來(lái)讀取和分發(fā)鍵 盤(pán)消息的了。這里的InputDispatcherThread線程對(duì)象mDispatcherThread和InputReaderThread線程對(duì) 象是在前面的Step9中創(chuàng)建的,調(diào)用了它們的run函數(shù)后,就會(huì)進(jìn)入到它們的threadLoop函數(shù)中去,只要threadLoop函數(shù)返回true,函數(shù) threadLoop就會(huì)一直被循環(huán)調(diào)用,于是這兩個(gè)線程就起到了不斷地讀取和分發(fā)鍵盤(pán)消息的作用。
Step9 在下來(lái)繼續(xù)看loopOnce()這個(gè)函數(shù)。 點(diǎn)擊(此處)折疊或打開(kāi)
這里面需要注意像神一樣的函數(shù) mEventHub->getEvents()。其實(shí)現(xiàn)原理,還有點(diǎn)不是很清楚;但是其功能就是負(fù)責(zé)鍵盤(pán)消息的讀取工作,如果當(dāng)前有鍵盤(pán)事件發(fā)生或者有鍵盤(pán)事件等待處理,通過(guò)mEventHub的 getEvent函數(shù)就可以得到這個(gè)事件,然后交給processEventsLocked 函數(shù)進(jìn)行處理。同樣需要特別注意最后一行;后面回解釋。我們還會(huì)回來(lái)的~~~
點(diǎn)擊(此處)折疊或打開(kāi)
函數(shù)原型! 在成功獲取input Event之后,就會(huì)用到 processEventsLocked函數(shù)來(lái)處理Event 然后在調(diào)用到 processEventsForDeviceLocked(deviceId,rawEvent, batchSize); 最后在void InputDevice::process(constRawEvent* rawEvents, size_t count) 我就在想:?jiǎn)柺裁床恢苯拥絧rocess函數(shù)呢?其實(shí)我覺(jué)得這里體現(xiàn)了設(shè)計(jì)模式中的單一職責(zé)原則;這種設(shè)計(jì)可以有效的控制函數(shù)粒度(有個(gè)類(lèi)粒度,這里自創(chuàng)函數(shù)粒度)的大小,函數(shù)承擔(dān)的職責(zé)越多其復(fù)用的可能性就越小,并且當(dāng)期中某一個(gè)職責(zé)發(fā)生變化,可能會(huì)影響其他職責(zé)的運(yùn)作!
Step 10 接下來(lái)繼續(xù)看InputDevice::process函數(shù)。點(diǎn)擊(此處)折疊或打開(kāi)
走到這里才算是真真正正的知道了有按鍵發(fā)生了,調(diào)用 KeyboardInputMapper::process(const RawEvent*)處理input event; KeyboardInputMapper 繼承自 InputMapper。那為什么調(diào)用的是 KeyboardInputMapper而不是SwitchInputMapper等等。。 請(qǐng)留意 點(diǎn)擊(此處)折疊或打開(kāi)
函數(shù)中的片段: 點(diǎn)擊(此處)折疊或打開(kāi)
這里Event Type有必要提一下,以下是一些常用的Event。在kernel/Documentation/input/event-codes.txt中有詳細(xì)的描述。 * EV_SYN: - Used as markers to separate events. Eventsmay be separated in time or in space, such as with the multitouch protocol. * EV_KEY: - Used to describe state changes ofkeyboards, buttons, or other key-like devices. * EV_REL: - Used to describe relative axis value changes,e.g. moving the mouse 5 units to the left. * EV_ABS: - Used to describe absolute axis valuechanges, e.g. describing the coordinates of a touch on a touchscreen. * EV_MSC: - Used to describe miscellaneous input datathat do not fit into other types. * EV_SW: - Used to describe binary stateinput switches. Step 11 點(diǎn)擊(此處)折疊或打開(kāi)
在這里,先判斷isKeyboardOrGamepadKey(scanCode),然后在用getEventHub()->mapKey()檢測(cè) 提供的key是否正確,在然后就開(kāi)始處理了processKey Step 12 點(diǎn)擊(此處)折疊或打開(kāi)
不用多解釋了,直接notifyKey了。。但需要注意,這里的notifyKey 僅僅是 NotifyKeyArgs push到消息隊(duì)列中去;并沒(méi)有通知上層!那到底在那兒通知的呢?
還記不記得在void InputReader::loopOnce()這個(gè)函數(shù)的最后一行代碼,其實(shí)質(zhì)是在這個(gè)函數(shù)中通知上層有按鍵事件發(fā)生。
這個(gè)flush()很明顯,notify了之后,就delete,不存在了。問(wèn)什么不是在getListener()->notifyKey(&args);的時(shí)候就真正的notify?我覺(jué)得可以做如下角度予以考慮: 第一:線程是最小的執(zhí)行單位;因此每當(dāng)inputThread.start()的時(shí)候,如果不flush,回造成數(shù)據(jù)混亂。 第二:flush操作是必須的,同時(shí)在loopOnce的最后操作也是最恰當(dāng)?shù)摹F鋵?shí)這里的Listener也就是充當(dāng)了一個(gè)事件分發(fā)者的角色。
這說(shuō)明,到這里已經(jīng)完全識(shí)別了按鍵了,并按照自己的鍵盤(pán)映射映射了一個(gè)值保存在args中,notifyKey給上層應(yīng)用了。。
Step 13 其實(shí)針對(duì)BACK HOME MENU這三個(gè)按鍵來(lái)說(shuō),其實(shí)質(zhì)就是TouchScreen;因此在inputReader.cpp中獲取Touch映射是在函數(shù)boolTouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) 中。這里同上面的Step 12相同。 ![]() 首先檢測(cè)不是多點(diǎn)Touch。然后使用const TouchInputMapper::VirtualKey*TouchInputMapper::findVirtualKeyHit( int32_t x, int32_t y)依據(jù)坐標(biāo)值查找出Touch的映射值。 |
|
來(lái)自: 昵稱(chēng)2009261 > 《我的圖書(shū)館》