Framework學(xué)習(xí)之input觸摸事件原理
這幾天修改input驅(qū)動(dòng),InputManagerService過程介紹下:
1、輸入驅(qū)動(dòng)系統(tǒng)簡(jiǎn)單介紹
- Android設(shè)備可以同時(shí)連接多個(gè)輸入設(shè)備,比如說觸摸屏,鍵盤,鼠標(biāo)等等;
- 用戶在任何一個(gè)設(shè)備上的輸入就會(huì)產(chǎn)生一個(gè)中斷,經(jīng)由Linux內(nèi)核的中斷處理以及設(shè)備驅(qū)動(dòng)轉(zhuǎn)換成一個(gè)Event,并傳遞給用戶空間的應(yīng)用程序進(jìn)行處理;
- 每個(gè)輸入設(shè)備都有自己的驅(qū)動(dòng)程序,數(shù)據(jù)接口也不盡相同,如何在一個(gè)線程里(上面說過只有一個(gè)InputReader Thread)把所有的用戶輸入都給捕捉到? 這首先要?dú)w功于Linux 內(nèi)核的輸入子系統(tǒng)(Input Subsystem);
- 它在各種各樣的設(shè)備驅(qū)動(dòng)程序上加了一個(gè)抽象層,只要底層的設(shè)備驅(qū)動(dòng)程序按照這層抽象接口來實(shí)現(xiàn),上層應(yīng)用就可以通過統(tǒng)一的接口來訪問所有的輸入設(shè)備;
- 這個(gè)抽象層有三個(gè)重要的概念,input handler, input handle 和 input_dev,
- input_dev代表底層驅(qū)動(dòng)
- input_handler代表某類輸入設(shè)備的處理方法,相當(dāng)于一個(gè)上層驅(qū)動(dòng)
- 一個(gè)input_dev 可以有多個(gè)input_handler,同樣,一個(gè)input_handler 可以用于多種輸入設(shè)備;
- 用來關(guān)聯(lián)某個(gè)input_dev 和 某個(gè) input_handler, 它對(duì)應(yīng)上圖中的紫色的原點(diǎn)。每個(gè)input handle 都會(huì)生成一個(gè)文件節(jié)點(diǎn);
通過Linux input system獲取用戶輸入的流程簡(jiǎn)單如下:
- 設(shè)備通過input_register_dev 將自己的驅(qū)動(dòng)注冊(cè)到Input 系統(tǒng)。
- 各種Handler 通過 input_register_handler將自己注冊(cè)到Input系統(tǒng)中。
- 每一個(gè)注冊(cè)進(jìn)來的input_dev 或 Input_handler 都會(huì)通過input_connect() 尋找對(duì)方,生成對(duì)應(yīng)的 input_handle,并在/dev/input/下產(chǎn)成一個(gè)設(shè)備節(jié)點(diǎn)文件.
- 應(yīng)用程序通過打開(Open)Input_handle對(duì)應(yīng)的文件節(jié)點(diǎn),打開其對(duì)應(yīng)的input_dev 和 input_handler的驅(qū)動(dòng)。這樣,當(dāng)用戶按鍵時(shí),底層驅(qū)動(dòng)就能捕捉到,并交給對(duì)應(yīng)的上層驅(qū)動(dòng)(handler)進(jìn)行處理,然后返回給應(yīng)用程序。
2、InputManagerService詳解
2.1、InputManagerService啟動(dòng)
InputManagerService是Android為了處理各種用戶操作而抽象的一個(gè)服務(wù),自身可以看做是一個(gè)Binder服務(wù)實(shí)體,在SystemServer進(jìn)程啟動(dòng)的時(shí)候?qū)嵗?,并注?cè)到ServiceManager中去,不過這個(gè)服務(wù)對(duì)外主要是用來提供一些輸入設(shè)備的信息的作用,作為Binder服務(wù)的作用比較小
InputManagerService跟WindowManagerService幾乎同時(shí)被添加,從一定程度上也能說明兩者幾乎是相生的關(guān)系;
而觸摸事件的處理也確實(shí)同時(shí)涉及兩個(gè)服務(wù),最好的證據(jù)就是WindowManagerService需要直接握著InputManagerService的引用;
如果對(duì)照上面的處理模型,InputManagerService主要負(fù)責(zé)觸摸事件的采集;
而WindowManagerService負(fù)責(zé)找到目標(biāo)窗口。接下來,先看看InputManagerService如何完成觸摸事件的采集;
2.2、如何捕獲觸摸事件
InputManagerService會(huì)單獨(dú)開一個(gè)線程專門用來讀取觸摸事件,
- 這里有個(gè)EventHub,它主要是利用Linux的inotify和epoll機(jī)制;
- 監(jiān)聽設(shè)備事件:包括設(shè)備插拔及各種觸摸、按鈕事件等,可以看做是一個(gè)不同設(shè)備的集線器,主要面向的是/dev/input目錄下的設(shè)備節(jié)點(diǎn),比如說/dev/input/event0上的事件就是輸入事件,通過EventHub的getEvents就可以監(jiān)聽并獲取該事件:
在new InputManager時(shí)候,會(huì)新建一個(gè)InputReader對(duì)象及InputReaderThread Loop線程,這個(gè)loop線程的主要作用就是通過EventHub的getEvents獲取Input事件
輸入事件就可以被讀取,經(jīng)過processEventsLocked被初步封裝成RawEvent,最后發(fā)通知,請(qǐng)求派發(fā)消息;
2.3、事件的派發(fā)
- 在新建InputManager的時(shí)候,不僅僅創(chuàng)建了一個(gè)事件讀取線程;
- 還創(chuàng)建了一個(gè)事件派發(fā)線程,雖然也可以直接在讀取線程中派發(fā),但是這樣肯定會(huì)增加耗時(shí),不利于事件的及時(shí)讀??;
- 因此,事件讀取完畢后,直接向派發(fā)線程發(fā)個(gè)通知,請(qǐng)派發(fā)線程去處理,這樣讀取線程就可以更加敏捷,防止事件丟失,因此InputManager的模型就是如下樣式:
InputReader的mQueuedListener其實(shí)就是InputDispatcher對(duì)象,所以mQueuedListener->flush()就是通知InputDispatcher事件讀取完畢,可以派發(fā)事件了, InputDispatcherThread是一個(gè)典型Looper線程,基于native的Looper實(shí)現(xiàn)了Hanlder消息處理模型,如果有Input事件到來就被喚醒處理事件,處理完畢后繼續(xù)睡眠等待,代碼如下:
以上就是派發(fā)線程的模型,dispatchOnceInnerLocked是具體的派發(fā)處理邏輯,這里看其中一個(gè)分支,觸摸事件:
從以上代碼可以看出,對(duì)于觸摸事件會(huì)首先通過findTouchedWindowTargetsLocked找到目標(biāo)Window,進(jìn)而通過dispatchEventLocked將消息發(fā)送到目標(biāo)窗口;
2.4、總結(jié)
現(xiàn)在把所有的流程跟模塊串聯(lián)起來,流程大致如下:
- 點(diǎn)擊屏幕
- InputManagerService的Read線程捕獲事件,預(yù)處理后發(fā)送給Dispatcher線程
- Dispatcher找到目標(biāo)窗口
- 通過Socket將事件發(fā)送到目標(biāo)窗口
- 找到目標(biāo)窗口處理事件