一次觸摸,Android到底幹了啥

來源:互聯網
上載者:User

作者:任韜,騰訊移動用戶端開發 進階工程師
商業轉載請聯絡騰訊WeTest獲得授權,非商業轉載請註明出處。
原文連結:http://wetest.qq.com/lab/view/349.html WeTest 導讀

當我們在寫帶有UI的程式的時候,如果想擷取輸入事件,僅僅是寫一個回呼函數,比如(onKeyEvent,onTouchEvent….),輸入事件有可能來自按鍵的,來自觸摸的,也有來自鍵盤的,其實軟鍵盤也是一種獨立的輸入事件。那麼為什麼我能通過回呼函數擷取這些輸入事件呢。系統是如何精確的讓程式獲得輸入事件並去響應的呢。為什麼系統只能同一時間有一個介面去獲得觸摸事件呢。 下面我們通過Android系統輸入子系統的分析來回答這些問題。 一、輸入事件的轉寄流程

二、物理裝置是如何將輸入資料發送給核心的

物理裝置將資料發送給核心是通過裝置驅動傳輸的,在linux下的/dev/input/目錄下有幾個裝置檔案,event0,event1,event2……… 這些裝置檔案實際上是驅動建立的,他們共用一個主裝置號,僅僅是次裝置號不同,表示這是一類裝置。比如觸控螢幕對應event0,觸控螢幕驅動被掛載後,驅動程式會進行初始化,主要是初始化CPU引腳,設定中斷處理常式。

很好理解,觸控螢幕是一個物理裝置,但是我們的驅動程式運行在CPU中,這是兩個不同的裝置,他們在物理上的串連是通過導線將對應的引腳相串連的,只不過導線在PCB板中很小,驅動程式就是初始化CPU中跟觸控螢幕串連的引腳,但讓每個引腳都會對應寄存器,這個在CPU的晶片手冊中很詳細(DataSheet)。

當按下觸控螢幕的時候觸控螢幕有個引腳電平變低了,相連的CPU引腳檢查到這個串連的引腳電壓變低了,那麼就會觸發中斷,這個在觸摸驅動中初始化好的,CPU有個中斷向量表,這裡就到了我們驅動中寫好的中斷處理函數,中斷處理函數中就會讀取觸控螢幕的資料,就是通過相串連的引腳組成的位元據比如(01011010),這個時候我們的核心就拿到的觸控螢幕的資料。

觸控螢幕晶片的時序圖 三、核心是如何把輸入資料發送給使用者空間Android framework的

核心拿到觸控螢幕的資料後,經過平滑處理,濾波,資料還是在核心空間,那麼Android怎麼拿到觸摸資料呢。 Android實際上是運行在linux核心上一組進程,這一組進程組合為使用者提供UI,應用程式的安裝等等服務。

手機開機流程是linux核心先啟動,啟動完成之後會將Android進程組啟動起來,FrameWork屬於這個進程組之中。Framewok中有個服務InputManagerService,我們看Android源碼它在哪裡執行個體化的:

 SystemServer.java----------->          startOtherServices()------>          /*構造InputManagerService*/          inputManager = new InputManagerService(context);             /*將inputManager傳遞給WindowManagerService去             wm=WindowManagerService.main(context, inputManager,            mFactoryTestMode !=FactoryTest.FACTORY_TEST_LOW_LEVE !mFirstBoot, mOnlyCore);             /*給InputManagerService設定回調*/             inputManager.setWindowManagerCallbacks(wm.getInputMonitor());              /* 全初始化好後,SystemServer調用start()函數讓InputManager中兩個線程開始運行。先看InputReaderThread,它是事件在使用者態處理過程的起點*/             inputManager.start();

所以可以看到它在SystemServer進程中執行個體化並且啟動,所以我們首先需要看看InputManagerService的建構函式都做了什麼。

建構函式會調用到jni建立NativeInputManager的c++對象, NativeInputManager建構函式中建立

Sp eventHub = new EventHub()

mInputManager = new InputManager(eventhub,this,this);

eventHub物件建構函數做了下面幾件事情:

建立epoll對象,之後就可以把各個輸入裝置的fd添加進來多路等待輸入事件

利用inotify機制監聽/dev/input目錄下的變更,如果有則意味著裝置變換,需要處理,輸入裝置的增減刪除操作的監聽,將代表inotify的fd添加到epoll中

建立pipe,管道只能用來在具有公用祖先的兩個之間通訊.讀端添加epoll中

InputManager物件建構函數做了下面幾件事:

建立InputDispatcher

建立InputReader(eventhub,inputdispatcher),InputDispatcher繼承InputListenerInterface

建立InputReaderThread

建立InputDispatcherThread

我們還記得最SystemServer.java中最後通過inputManager.start(); 來運行我們的InputManagerService,所以繼續看start方法,實際上在native層的inputManager對象中,將上面建立的兩個線程InputReaderThread和InputDispatcherThread的start方法中。

對於InputReaderThread的start方法: 調用建構函式中儲存的eventHub的getEvents方法擷取input事件,在getEvent方法中做的事

1)判斷是不是需要開啟input裝置驅動,如果需要開啟裝置驅動,掃描/dev/input目錄下的裝置檔案並開啟這些裝置,同時會判斷裝置列表中有沒有虛擬鍵盤,沒有的話就建立一個device添加進去

2)到下一步中至少系統存在兩個輸入裝置,一個是觸控螢幕,一個是虛擬鍵盤,因為上面這次getEvent的調用需要開啟裝置,所有就將這些動作封裝成RawEvent事件,這裡兩個DEVICE_ADDED事件+FINISH_DEVICE_SCAN事件,將這些事件返回,不會往下走了

3)如果第二次進入getEvents方法中就會等待讀取輸入事件,將讀取的touch事件發送返回

到這裡我們就知道了核心空間的觸摸輸入資料是如何傳遞到了使用者空間的Android framework中的,實際上就是通過/dev/input目錄下,去掃描這個目錄,如果有device就開啟這個device ,並添加到epoll對象中,多路等待輸入事件,在loop中擷取資料。 四、Android framework是怎樣將輸入資料發送給APP進程的

Android framework擷取了觸摸輸入的資料,但是在系統中有那麼多進程,那麼多進程都在擷取輸入,它是如何進一步處理,準確的分發事件的呢。

InputReaderThread的start方法中做的第二件事情:

調用processEventsLocked方法處理上面的getEvents方法返回的的RawEvent

1)根據RawEvent的類型不同,調用不同的方法處理,有 ● 普通的touch事件

.● 添加裝置的事件

.● 刪除裝置的事件

.● FiNISHED_DEVICE_SCAN

2)對於touch事件: 調用這個touch事件對應的輸入裝置(之間建立的InputDevice)的process方法,該方法內部調用內部的InputMapper的process方法,一個輸入裝置有很多個Mapper,遍曆所有的Mapper,並調用process,假定我們是一個支援多點觸摸的touch screen,它的mapper是MultiTouchInputMapper,調用它的process方法。

3)MultiTouchInputMapper的process方法內部會這樣處理:

首先每次一個touchEvent擷取Slot,在沒有收到EV_SYN之前對應的Slot都是相同的,然後依次處理x,y,pressure,touch_major,這些值初始化slot的各個變數;

當收到ev.type== EV_SYN並且ev.code = SYN_MT_REPORT那麼當前的slot的index加1,給下一次觸摸事件去記錄,同時sync函數處理這次觸摸事件;

然後CurrentCookedPointerData和LastCookedPointerData進行一些列的操作,up,down還是move事件,然後對應的不同事件,調用dispatchMotion,內部調用InputDispatcher的notifyMotion

4)對於InputDispatcher的notifyMotion:

● 如果InputDispatcher設定了inputFilter,那麼首先調用inputFilter來消費這些事件

● 如果沒有inputFiler,或者inputFilter對這些事件不感興趣,那麼就會構造一個MotionEntry,添加到mInboundQueue,並喚醒InputDispatcher線程處理

5)對於InputDispatcher的線程處理迴圈:

● 最佳化app切換延遲,當切換逾時,則搶佔分發,丟棄其他所有即將要處理的事件;

● 分發事件:

首先調用findTouchedWindowTagetsLocked尋找有focus的window視窗, 並把這些建立儲存在inputTargets數組中;

之前註冊的monitor的InputChannel這裡也會添加到inputTargets數組中;

然後向inputTargets數組一一分發事件。

到這裡我們就知道了是如何找到這個APP進程的了。 五、APP進程是如何將輸入資料發送給它對應的Activity的

Activity是一個進程的基本組件,可以認為它代表了一個介面,是一堆View的集合,每次Activity啟動的時候都做了什麼呢。

1、實際上取決於它背後的ViewRootImpl做了什麼,在ViewRootImpl.java中的setView方法中,執行個體化InputChannel,當然會判斷當前的視窗能不能接受輸入事件,接著在調用到session.java中的addToDisplay方法傳遞給WindowManagerService,實際上是調用WindowManagerService的addWindow方法,在WindowManagerService中會建立一對InputChannel[],然後InputChannel[1]轉移到這個inputChannel, 然後setView方法繼續建立一個WindowInputEventReceiver對象,然後將上面建立好的InputChannel

2、WindowManagerService中的addWindow方法:

InputChannel[] inputChannels = InputChannel.openInputChannelPair(name)/*Channel[0]儲存在server端*/win.setInputChannel[inputChannels[0]]/* Channel[1]返回給ViewRootImpl端*/inputChannels[1].transferTo(outInputChannel)/*註冊到inputManagerService中*/

mInputManager.registerInputChannel(win.mInputChannel,win.mInputWindowHandle)

到這裡我們就能明白如何將時間分發給對應的Activity了,其實是給了它背後的ViewRootImpl。 六、Activity又是如何將輸入資料發送給具體的View的

最後一步就是將事件分發到Activity中具體的View了,從ViewRootImpl中將事件分發給具體的View,很好理解,因為觸摸的範圍在到這裡是知道的,每個View的位置以及狀態到這裡也是知道的,因為View要正確渲染的話,Android圖形架構會搞定這一切,測量每個View的大小,確定每個View的位置,ViewRootImpl會一層一層將資料分發到自己每個View中,但是每個View自己知道這個觸摸事件是不是作用在自己身上的,如果不是就丟棄了,往下面分發。 總結

觸摸事件的分發流程看起來挺複雜,但是Android實現的還是很優雅的,我們去分析它的流程,對於我們想實現一些比較的酷的功能是有協助的。當然對於我們調試代碼也會有協助,當發現觸摸後,系統無響應,將上面的流程分解,總是能分析出原因。

騰訊WeTest提供上千台真實手機,隨時隨地進行測試,保障應用/手遊品質。節省百萬硬體費用,加速敏捷研發流程。

同時騰訊WeTest相容性測試團隊積累了10年的手遊測試經驗,旨在通過制定針對性的測試方案,精準選取目標機型,執行專業、完整的測試案例,來提前發現遊戲版本的相容性問題,針對性地做出修正和最佳化,來保障手遊產品的品質。目前該團隊已經支援所有騰訊在研和運營的手遊項目。

歡迎進入:http://wetest.qq.com/product/cloudphone 體驗安卓真機

歡迎進入:http://wetest.qq.com/product/expert-compatibility-testing 使用專家相容測試服務。 WeTest相容性測試團隊期待與您交流。You Create,We Test。

如果對使用當中有任何疑問,歡迎聯絡騰訊WeTest企業QQ:800024531

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.