【Android】Android輸入子系統

Leo.cheng發表於2014-01-05

成鵬致遠 | lcw.cnblogs.com | 2013-10-25

Linux輸入子系統回顧

1:為什麼要回顧linux輸入子系統?這個問題後面自然就知道了

  1.linux輸入子系統裝置是基於平臺裝置機制的,所以先回顧平臺裝置機制,主要回顧後面用得到的東西

1.申請主裝置號
2.建立cdev->將cdev掛載到系統裝置雜湊連結串列中,同時生成inode節點
3.建立device->將device與剛生成的inode節點關聯起來,為上層呼叫提供介面

  2.註冊輸入子系統裝置

1.建立一個裝置類class
2.申請主裝置號
3.建立cdev->將cdev掛載到系統裝置雜湊連結串列中,同時生成inode節點
4.建立input_device->將input_device與剛生成的inode節點關聯起來,為事件驅動層提供介面

1.註冊裝置支援輸入事件型別(type)->【這個後面會用到】
2.註冊裝置支援輸入事件編碼
3.生成/dev/input_device

      5.當input_device與input_hander匹配成功

1.生成/dev/input*
2.上層應用通過主裝置號開啟/dev/input,通過次裝置號開啟/dev/input_device

  3.輸入子系統部分函式

1.read函式->讀【後面會用到】
2.write函式->寫【後面會用到】
3.event函式->上報事件【後面會用到】


【提前思考的問題】

1.Android事件處理系統是怎麼捕捉到輸入事件的?
2.Android上層應用是怎麼捕捉到輸入事件的?
3.Android上層應用捕捉到輸入事件是怎麼響應的?


2.從啟動一個Android程式開始

  1.Activity啟動流程:onCreate()->onStart()->onResume()->Activity Running

【Q1】:為什麼onStart()後不直接Running,要插入一個onResume(),在onResume()中系統作了什麼事情?
<A1>:在onResume()中,系統會為該Activity建立一個ViewRoot!

  【Q2】:這個ViewRoot有什麼用?它做了哪些事情?這個後面解答


3.Android事件從輸入到輸出的整個流程

  1.Activity執行時,使用者點選觸控式螢幕操作->{事件產生
    1.點選觸控式螢幕,必然會呼叫觸控式螢幕驅動->{事件輸入

【Q3】:事件傳遞過程?->{事件傳遞}
<A3>:以下所有步驟!
 1.Android上層應用呼叫Framework層的JNI本地方法->{事件訊息傳遞到JNI層

 1.實現JNI層方法,填充本地方法對映表,實現提供Android上層應用的介面
 2.生成so動態連結庫檔案,adb push到/system/lib目錄下

 2.JNI本地方法呼叫HAL層(硬體抽象層:中介軟體)方法->{事件訊息傳遞到HAL層

 1.JNI層通過指定ID得到HAL模組例項,然後呼叫HAL層函式
 2.生成so動態連結庫檔案,adb push到/system/lib/hw目錄下

 3.HAL通過系統呼叫write進入核心層->{事件訊息傳遞到核心

 1.copy_from_user()取得使用者層資料
 2.呼叫input_device.write->{(1.1.2):事件訊息傳遞到驅動層

------------------------------------------------------------------------------------------
 4.輸入裝置驅動獲取事件,呼叫event函式->{(1.1.3)}
 5.事件處理層根據事件型別進行上報->{事件訊息傳遞到核心事件處理層
   1.呼叫input_event(device,type,code,value)->{【type】後面會用到}
 6.到事件處理層時,核心會喚醒read函式->{(1.1.1):事件訊息傳遞到核心層
   1.通過copy_to_user()將核心資料傳遞到使用者空間->{事件訊息傳遞到使用者空間
    【Q4】:read函式被誰呼叫的?
 7.事件訊息被Android的事件處理系統捕捉到->{事件訊息傳遞到Android事件處理系統

 1.Android事件處理系統將這個訊息傳送到Android應用層
 【Q5】:事件訊息在Android事件處理系統中是怎麼傳遞的?

 8.Android上層獲取事件訊息,根據事件型別(3.1.1.5.1:type)響應上層View相應回撥函式
 9.Android介面UI更新->{事件響應


4.至此為此,專案中用到的框架知識已經介紹完畢

  1.Android事件處理系統作為一個黑盒子,暫時略過
  2.切入到專案解讀:電視棒遠端遙控器

1.客戶端APK

1.資料採集->{此資料必須與Linux輸入子系統相容,這樣才能達到欺騙系統的目的}
2.資料傳遞

2.服務端APK->{實現二個虛擬裝置(虛擬鍵盤裝置與虛擬滑鼠裝置}

1.接收資料
2.將資料傳送到虛擬裝置
3.虛擬裝置上報事件,欺騙系統輸入事件發生


5.剖析Android事件處理系統

【Q5】:事件訊息在Android事件處理系統中是怎麼傳遞的?
<A5>:看看Android的事件處理系統
1.緊接著3.1.1.7,事件訊息傳遞到Android事件處理系統
2.在Android開機時,系統服務(System Server)會初始化Android視窗管理服務(WindowManagerService
3.WMS服務會初始化InputManagerInputManager是Framework層的一個C++類,負責管理所有的輸入事件的捕獲與轉發

1.在InputManager建構函式中,會初始化事件處理系統中最重要的三個類(InputReader/InputDispatcher/EventHub
2.然後初始化InputManager(InputManager.initialize()),產生兩個執行緒(InputReaderThread/InputDispatcherThread,分別負責事件捕獲與事件轉發)
3.InputReaderThread執行緒工作->{解決問題1:Android事件處理系統是怎麼捕捉到輸入事件的}

    1.呼叫InputReader::loopOnce(),在InputReader類中,已經實現looper機制,迴圈操作
      1.呼叫EventHub::getEvent()函式
        1.呼叫Epoll_wait()函式
          1.在Epoll_wait()函式中,會去迴圈讀取所有的/dev/input裝置,一旦有事件產生,就會被此函式捕捉到

【Q6】:Android事件處理系統與/dev/input裝置是如何關聯起來的
A6】:EventHub::Device <---->/dev/input,一一對應關係

 2.當有事件發生時,Android事件處理系統會呼叫read函式->{系統呼叫,從核心層拿到事件訊息}

【Q4】:read函式被誰呼叫的?
<A4>:read函式是InputReader呼叫的

      2.將從核心層讀取到的事件訊息(input_event)儲存到RawEvent中

2.根據事件型別(input_event.type)呼叫相應的訊息轉換器(InputMapper
  1.Android系統目前支援5種事件訊息(翻滑蓋/軌跡球/多點觸控/單點觸控/鍵盤)
3.InputReader::InputDevice::inputMapper將RawEvent事件轉換成對應的Notifyargs
  1.InputMapper將RawEvent事件轉換成對應的Notifyargs類
  2.將Notifyargs加入到InputRead::QueueListener::argsQueue佇列中
4.呼叫InputReader::QueueListener::flush()函式,處理佇列中的事件訊息
5.呼叫Notifyargs::notify函式,將事件訊息轉換成對應的EventEntry再轉交給InputDispatcher

1.對事件進行預處理
  1.判斷是否丟棄此事件訊息
    1.丟棄,則直接返回
    2.不丟棄
  2.獲取當前Activity對應的Connection物件->(這個後面再講)
2.將對應的EventEntry加入到InputDispatcher::OutBoundQueue佇列中
  【Q7】:在Notifyargs是怎麼獲取InputDispatcher例項的
  <A7>:在初始化InputRead::QueueListener::argsQueue佇列時,將InputDispatcher物件傳遞過來了

6.喚醒InputDispatcher::pollOnce()函式

4.InputDispatcherThread執行緒工作
  1.呼叫InputDispatcher::loopOnce(),在InputDispatcher類中,同樣實現looper機制,迴圈操作
    1.呼叫pollOnce()函式,輪詢InputDispatcher::OutBoundQueue佇列
      1.佇列中的有事件訊息
      2.被Notifyargs::notify()函式喚醒
  2.呼叫對應的InputDispatcher::dispach函式
    1.從InputDispatcher::OutBoundQueue佇列中取得EventEntry物件
    2.將EventEntry物件轉換成DispatchEntry物件
    3.將DispatchEntry物件加入到InputChannel::Connection::outboundQueue佇列中
      【Q8】:這個InputChannel::Connection物件是從哪裡來的?有什麼用?
【Q9】:現在InputDispatcher得到了轉換後的事件訊息,即將要發出去,但是往哪裡發?
<A9>:要解決這個問題,需要回到【Q2】


4.【Q2】:ViewRoot有什麼用?它做了哪些事情?->{解決問題2:Android上層應用是怎麼捕捉到輸入事件的?
  <A2>:1.首先,ViewRoot是一個Hander,與當前Activity綁在一起的
      2.ViewRoot有一個重要的作用:與WMS通訊,完成整個GUI視窗系統的繪製
      3.建立ViewRoot的時候做了哪些事情?現在來解決問題2:Android上層應用是怎麼捕捉到輸入事件的?

1.由前面分析知道,Android的事件輸入來自InputManager,所以ViewRoot需要與InputManager通訊
  【Q10】:ViewRoot怎麼與InputManager通訊並取得事件訊息?
  <A10>:ViewRoot與InputManager之間有一個共享記憶體(ShareMemory
    1.InputManager::InputDispatcher將最後的事件訊息傳送到共享記憶體(ShareMemory)中
    2.ViewRoot在知道有事件訊息到來時,就去共享記憶體(ShareMemory)中取此事件訊息
  【Q11】1.ViewRoot怎麼知道有事件訊息到來?
  <A11>:1.ViewRoot與InputManager是通過管道通訊的機制來傳遞訊息的
    1.在建立ViewRoot後,會建立兩個InputChannel類物件
      1.其中一個InputChannel物件註冊到NativeInputQueue中,與ViewRoot綁在一起
        【Q12】這個NativeInputQueue是用來幹什麼的?
        <A12>:這個NativeInputQueue是Android系統用來維護事件接收的,因為同一時刻,會有很多Activity在等待事件輸入
      2.另一個InputChannel物件註冊到InputManager類物件中
    2.同時會申請上面用到的的共享記憶體
    3.InputChannel類主要封裝管道描述符和共享記憶體的描述符等資訊
      1.在ViewRoot與InputManager中各註冊了一個InputChannel類物件,其中各有兩個管道描述符
        1.兩個InputChannel物件中都包含一個讀和一個寫描述符
        2.所以在ViewRoot與InputManager之前完成了全雙工的通訊【後面會用到】
  【Q13】:在Android中,每建立一個Activity,就會建立一個ViewRoot,所以也會建立一個InputChannel物件,那Android系統怎麼來區分這些Activity?
  <A13>:還記得【Q8】嗎,兩個問題一起解決:InputChannel::Connection物件是從哪裡來的?有什麼用?
    1.為了區分不同的Activity,NativeInputQueue類中定義了一個子類Connection
    2.在註冊InputChannel物件時,每個InputChannel物件中都建立了一個Connection物件
      1.所以ViewRoot中的每個InputChannel與InputManager中的InputChannel都包含一個Connection物件
      2.這個Connection物件標識了不同的Activity

        4.到現在我們就可以回到【Q9】了

5.【Q9】:現在InputDispatcher得到了轉換後的事件訊息,即將要發出去,但是往哪裡發?
 <A9>:1.由上面的分析,我們知道現在需要將事件訊息傳送給ViewRoot
    1.呼叫InputChannel::Connection::inputPublisher.publishMotionEvent函式將事件訊息傳送到共享記憶體(ShareMemory)
    2.InputChannel物件向寫管道傳送一個dispatch資訊

6.現在工作就該轉移到ViewRoot這邊了
  【Q14】:在NativeInputQueue中有很多InputChannel物件,究竟哪個InputChannel的管道會收到資訊?
  <A14>:還記得【Q13】嗎,InputManager的InputChannel物件是由ViewRoot建立後註冊過去的
    1.在每個註冊的InputChannel物件中,都包含了一個Connection物件
    2.InputManager的InputChannel物件的Connection物件<--->NativeInputQueue中的InputChannel物件的Connection物件,是一一對應的關係
    3.所以與InputDispatcher中Connection物件相對應的那個Connection物件將收到管道資訊
  1.相應的InputChannel物件的Connection物件收到管道資訊
  2.呼叫InputChannel::Connection::inputConsumer到共享記憶體(ShareMemory)中取得事件訊息
  3.InputChannel再向寫管道中發一個資訊,表示此事件已經取得
  4.然後InputManager中的InputChannel將會收到管道訊息,再繼續進行下一輪事件處理

7.到此為止,事件訊息已經傳遞到NativeInputQueue中的InputChannel物件中
  1.由前面分析,NativeInputQueue中的每個InputChannel物件都對應一個Activity
  2.NativeInputQueue中InputChannel物件是在ViewRoot建立之後建立的,也就是我們的Activity啟動之前
    【Q15】:現在Activity關聯的InputChannel物件拿到的這個事件訊息,但是怎麼處理呢?現在我們就來回答問題3:Android上層應用捕捉到輸入事件是怎麼響應的?
    <A15>:這個需要回到ViewRoot端InputChannel物件註冊的時候,也就是【Q11】的位置
      1.ViewRoot端InputChannel物件在向NativeInputQueue註冊時,需要註冊3個引數
        1.其中有一個引數就是ViewRoot的成員變數InputHandler
          1.InputHandler就是事件的處理函式,也就是所謂的回撥函式
          2.傳遞它的作用主要是明確當前ViewRoot的事件處理函式
          3.當InputChannel物件取得事件後,就會去呼叫ViewRoot的InputHandler函式-{到此為止,事件訊息就傳遞迴了Android應用層}
            【Q16】:這個回撥函式到底是什麼?
            <A17>:這個回撥函式大多被Android系統實現成抽象函式
              1.在我們的電視棒遠端遙控器的客戶端,就重寫了對應的方法,用來獲取我們需要的資料,比如onScroll/onLongPress等


 

思維導圖

相關文章