尊重原創轉載請注明: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節點:
1 2 Aigestudio>adb shell 3 shell@mx3:/ $ getevent 4 getevent 5 add device 1: /dev/input/event0 6 name: "mxhub-keys" 7 add device 2: /dev/input/event4 8 name: "lsm330dlc_gyr" 9 add device 3: /dev/input/event3 10 name: "lsm330dlc_acc" 11 add device 4: /dev/input/event1 12 name: "gp2ap" 13 could not get driver version for /dev/input/mouse0, Not a typewriter 14 add device 5: /dev/input/event5 15 name: "mx_ts" 16 add device 6: /dev/input/event6 17 name: "gpio-keys" 18 add device 7: /dev/input/event7 19 name: "Headset" 20 add device 8: /dev/input/event2 21 name: "compass" 22 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輸入設備類型包括如下這些:
1 #define EV_SYN 0x00 2 #define EV_KEY 0x01 3 #define EV_REL 0x02 4 #define EV_ABS 0x03 5 #define EV_MSC 0x04 6 #define EV_LED 0x11 7 #define EV_SND 0x12 8 #define EV_REP 0x14 9 #define EV_FF 0x15 10 #define EV_PWR 0x16 11 #define EV_FF_STATUS 0x17 12 #define EV_MAX 0x1f
具體它們都代表什么就不多說了,都是些Linux的東西,一般來說比較常用的是EV_REL表示相對坐標類型、EV_ABS表示絕對坐標類型、EV_KEY表示物理鍵盤事件類型,EV_SYN表示同步事件類型等等,一個設備可以支持多個不同的事件類型而每個事件類型呢又可以設置不同的事件碼,比如EV_SYN同步事件類型的事件碼如下:
1 #define SYN_REPORT 0 2 #define SYN_CONFIG 1 3 #define SYN_MT_REPORT 2
其它的就不一一列舉了都可以在input.h文件中找到相應的定義。上面圖例中的一次快速觸屏后的反饋信息可以做如下描述:
1 /dev/input/event5: EV_ABS ABS_MT_TRACKING_ID 000008e0 標志多點追蹤信息的采集開始(需要設備支持) 2 /dev/input/event5: EV_ABS ABS_MT_POSITION_X 00000280 上報接觸面的中心點X坐標 3 /dev/input/event5: EV_ABS ABS_MT_POSITION_Y 0000064b 上報接觸面的中心點Y坐標 4 /dev/input/event5: EV_ABS ABS_MT_PRESSURE 0000005b 上報手指壓力 5 /dev/input/event5: EV_ABS ABS_MT_TOUCH_MAJOR 00000014 上報主接觸面長軸 6 /dev/input/event5: EV_SYN SYN_REPORT 00000000 同步數據 7 /dev/input/event5: EV_ABS ABS_MT_PRESSURE 00000057 8 上報手指壓力 9 /dev/input/event5: EV_ABS ABS_MT_TOUCH_MAJOR 00000012 10 上報主接觸面長軸 11 /dev/input/event5: EV_SYN SYN_REPORT 00000000 同步數據 12 /dev/input/event5: EV_ABS ABS_MT_TRACKING_ID ffffffff 13 標志多點追蹤信息的采集結束(需要設備支持) 14 /dev/input/event5: EV_SYN SYN_REPORT 00000000 同步數據
如上過程只是一次快速觸碰所產生的節點讀取,如果我們做出更復雜的手勢操作比如多點切西瓜那樣的效果尼瑪光是采集這些信息都不得了!不過值得慶幸的是,對這些原始信息的采集用不着應用層的開發者來做,對於應用開發來說我們往往更關心一次事件是單擊呢還是雙擊還是長按等等,而不是面對這些龐大而又復雜的原始信息,So,Android在獲取到這些原始數據后會對其進行一定的轉化便於使用,當然如果你需要做驅動開發涉及到這些原始數據的操作也可以直接獲取其使用亦可。
可見,Linux中Input子系統對輸入設備信息的捕獲可以說是Android事件來源的老祖宗,當然這些玩意對於應用開發者來說沒必要深入理解,僅作了解即可。文章開頭我們曾這樣說過,一次事件的源頭必定來自於用戶的交互,那么事實上是不是如此呢?早年打過游戲的童鞋肯定對按鍵精靈這玩意很熟悉吧,至少不陌生,我們使用按鍵精靈來模擬用戶對鍵位的操作,也就是說我們並不一定需要用戶真實的交互,模擬也行。同樣地Android也給我們提供了另外一個很酷的工具sendevent來向/dev/input/寫入事件信息模擬事件的產生,具體用法跟getevent很類似,就不多說了,自行嘗試。