Android事件分發完全解析之事件從何而來,android從何而來
尊重原創轉載請註明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵權必究!
炮兵鎮樓
上一節Android事件分發完全解析之為什麼是她中我們簡略地分析了事件分發機制的由來,這裡要說明一點,Android(或者說任何的驅動系統)都包含大量不同類型的事件,比如按鍵啦、軌跡球啦、滑鼠啦、觸摸啦、紅外線啦等等等,這裡為了簡化問題也為了切合實際,我們只針對觸摸事件進行分析,至於其他的一些雜七雜八的事件其實都很好理解就不多說了。
那麼在Android中一個觸摸事件究竟是從何而來的呢?對事件分發稍有瞭解的童鞋一定知道dispatchtouchevent方法,都知道View對觸摸事件進行分發的起點,但是傳入dispatchtouchevent方法中的觸摸事件又是從何而來的呢?往上一步步追蹤你會發現代碼調用無窮無盡找不到頭……有時候盲目地去read fuck source code反而會讓你更困惑,其實用腦子想想理清邏輯就可以很快找到答案,我們都知道一個事件的產生肯定需要使用者的互動,也就是說,只有當使用者觸控螢幕幕或按下某個按鍵之類的操作之後系統才能做出事件響應,而每一個這樣的操作我們都可將其當作事件的“源頭”,那麼捕獲這些最原始互動資訊的獵手應該是誰呢?還會是View?還會是Activity?還會是ViewRootImpl還會是WMS嗎?這些framework中的構件相對於更底層的機制來說還是太“進階”了,我們知道Android是基於Linux的一款作業系統,Linux其本身就有一個很Perfect的Input子系統架構,Android雖然也實現了幾個屬於自己的機制,但是大部分底層的調用還是基於Linux所提供的操作介面,比如對Input驅動的編寫就是基於Linux Input系統字元驅動的操作介面,關於Linux的這部分大家如果有興趣可以去看看私房菜,這裡就不多扯了,這裡你僅需要知道在Android中Linux的Input子系統會在/dev/input/路徑下讀寫以event[NUMBER]為名的硬體輸入裝置節點。這些節點都是跟具體硬體有關的,所以呢可能每一款裝置的具體節點名都是不一樣的,比如在我的mx3中/dev/input/event0為mxhub-key而/dev/input/event1為gp2ap。具體的節點資訊可通過Android提供的getevent工具查看,如果你的裝置已經串連了PC或者模擬器已啟動,adb shell後getevent即可擷取事件讀寫的即時狀態,當然各個裝置是不一樣的,比如mx3中通過getevent查看所有Input節點:
Aigestudio>adb shellshell@mx3:/ $ geteventgeteventadd device 1: /dev/input/event0 name: "mxhub-keys"add device 2: /dev/input/event4 name: "lsm330dlc_gyr"add device 3: /dev/input/event3 name: "lsm330dlc_acc"add device 4: /dev/input/event1 name: "gp2ap"could not get driver version for /dev/input/mouse0, Not a typewriteradd device 5: /dev/input/event5 name: "mx_ts"add device 6: /dev/input/event6 name: "gpio-keys"add device 7: /dev/input/event7 name: "Headset"add device 8: /dev/input/event2 name: "compass"could not get driver version for /dev/input/mice, Not a typewriter
可見mx3中有8個Input子系統,分別為:
- 位於event0節點下讀寫魅族呼吸燈按鈕也就是螢幕下方圓形的那個發光主鍵的“mxhub-keys”子系統
- 位於event4節點下讀寫重力感應器的“lsm330dlc_gyr”子系統
- 位於event3節點下讀寫加速度感應器的“lsm330dlc_acc”子系統
- 位於event1節點下讀寫紅外線感應器的“gp2ap”子系統(魅族mx3是用紅外線來測定光感和距離的)
- 位於event5節點下讀寫螢幕觸摸的“mx_ts”子系統
- 位於event6節點下讀寫物理按鍵的“gpio-keys”子系統
- 位於event7節點下讀寫耳機按鍵的“Headset”子系統(有些手機監控線控裝置的系統常以hook為名,這裡魅族使用不多見Headset來表示該類不知是否是有布局頭戴式裝置的意義)
- 位於event2節點下讀寫羅盤的“compass”子系統
而mx3(不能說是Android哈這裡針對mx3)就是從這些系統節點中讀寫裝置的事件資訊,以上資訊我是在mx3滅屏時也就是按下電源鍵關閉螢幕後擷取的,如果我們再次按下電源點亮螢幕,核心驅動就會不斷地監控一些必要的讀寫事件,這裡我們不想讓我們的Terminal一直輸出,使用getevent的-c參數設定最大的輸出條數查看即可:
這裡我設定了最大16條輸出,亮屏後可見如上資訊顯示,如果不作輸出限制,Terminal就會一直輸出……也就是說加速度和紅外線感應器的子系統會不斷檢測外部環境的變化,至於為什麼,想想加速度感應和紅外感應我想大家都應該能心知肚明。如果我們在getevent後在螢幕上快速Touch一下,那麼event5節點下的子系統就回立即作出回應:
如中我們快速接觸螢幕後得到的資訊,可能不好懂對吧,給getevent加上-l參數格式化輸出看看:
註:因為硬體裝置、觸摸地區力度、期間等因素的影響你的輸出結果可能跟我不大一樣,以具體你具體的輸出為準,但輸出資訊大致是類似的。
這裡拿第一條資訊“/dev/input/event5: EV_ABS ABS_MT_TRACKING_ID 000008e0”來說,其中/dev/input/event5上面我們說了表示裝置節點;EV_ABS表示type事件類型;ABS_MT_TRACKING_ID表示code事件的掃描碼;000008e0則表示具體的事件值。這些資訊的定義都在kernel/include/linux/input.h檔案中作出了聲明,比如type輸入裝置類型包括如下這些:
#define EV_SYN 0x00#define EV_KEY 0x01#define EV_REL 0x02#define EV_ABS 0x03#define EV_MSC 0x04#define EV_LED 0x11#define EV_SND 0x12#define EV_REP 0x14#define EV_FF 0x15#define EV_PWR 0x16#define EV_FF_STATUS 0x17#define EV_MAX 0x1f
具體它們都代表什麼就不多說了,都是些Linux的東西,一般來說比較常用的是EV_REL表示相對座標類型、EV_ABS表示絕對座標類型、EV_KEY表示物理鍵盤事件類型,EV_SYN表示同步事件類型等等,一個裝置可以支援多個不同的事件類型而每個事件類型呢又可以設定不同的事件碼,比如EV_SYN同步事件類型的事件碼如下:
#define SYN_REPORT 0 #define SYN_CONFIG 1 #define SYN_MT_REPORT 2
其它的就不一一列舉了都可以在input.h檔案中找到相應的定義。上面圖例中的一次快速觸屏後的反饋資訊可以做如下描述:
/dev/input/event5: EV_ABS ABS_MT_TRACKING_ID 000008e0 標誌多點追蹤資訊的採集開始(需要裝置支援)/dev/input/event5: EV_ABS ABS_MT_POSITION_X 00000280 上報接觸面的中心點X座標/dev/input/event5: EV_ABS ABS_MT_POSITION_Y 0000064b 上報接觸面的中心點Y座標/dev/input/event5: EV_ABS ABS_MT_PRESSURE 0000005b 上報手指壓力/dev/input/event5: EV_ABS ABS_MT_TOUCH_MAJOR 00000014 上報主接觸面長軸/dev/input/event5: EV_SYN SYN_REPORT 00000000 同步資料/dev/input/event5: EV_ABS ABS_MT_PRESSURE 00000057上報手指壓力/dev/input/event5: EV_ABS ABS_MT_TOUCH_MAJOR 00000012上報主接觸面長軸/dev/input/event5: EV_SYN SYN_REPORT 00000000 同步資料/dev/input/event5: EV_ABS ABS_MT_TRACKING_ID ffffffff標誌多點追蹤資訊的採集結束(需要裝置支援)/dev/input/event5: EV_SYN SYN_REPORT 00000000 同步資料
如上過程只是一次快速觸碰所產生的節點讀取,如果我們做出更複雜的手勢操作比如多點切西瓜那樣的效果尼瑪光是採集這些資訊都不得了!不過值得慶幸的是,對這些原始資訊的採集用不著應用程式層的開發人員來做,對於應用開發來說我們往往更關心一次事件是單擊呢還是雙擊還是長按等等,而不是面對這些龐大而又複雜的原始資訊,So,Android在擷取到這些未經處理資料後會對其進行一定的轉化便於使用,當然如果你需要做驅動開發涉及到這些未經處理資料的操作也可以直接擷取其使用亦可。
可見,Linux中Input子系統對輸入裝置資訊的捕獲可以說是Android事件來源的老祖宗,當然這些玩意對於應用開發人員來說沒必要深入理解,僅作瞭解即可。文章開頭我們曾這樣說過,一次事件的源頭必定來自於使用者的互動,那麼事實上是不是如此呢?早年打過遊戲的童鞋肯定對按鍵精靈這玩意很熟悉吧,至少不陌生,我們使用按鍵精靈來類比使用者對鍵位的操作,也就是說我們並不一定需要使用者真實的互動,類比也行。同樣地Android也給我們提供了另外一個很酷的工具sendevent來向/dev/input/寫入事件資訊類比事件的產生,具體用法跟getevent很類似,就不多說了,自行嘗試。