android事件處理機制
『壹』 Framework事件機制——手撕Android事件處理的三種方法
Android的事件處理的三種方法:
setOnClickListener,setOnLongClickListener、setOnTouchListener
注意:如果onTouchEvent方法return true,則單擊事件和長摁事件不再執行;若onLongClick方法返回true,則單擊事件不再處理。
需要定義繼承組件的類,重寫回調方法Touch方法執行時,先被Activity捕獲,DispatchTouchEvent方法處理。return false,交給上層的onTouchEvent方法處理;return super.dispatchTouchEvent(ev),則傳遞給最外層的View。
View用Dispatch方法處理,return false,由上層的onTouchEvent方法處理。如果返回super.dispatchTouchEvent(ev),則本層的onInterceptTouchEvent攔截,如果攔截true,則攔截,false不攔截,傳遞給子View的DispatchTouchEvent處理。
常用的回調方法:onKeyDown,onKeyLongPress,onKeyUp,onTouchEvent,onTrackballEvent(軌跡球事件)監聽和回調同時存在時,先調用監聽。
流程模型圖:
Event source 事件源
Event 事件
Event Listener 事件監聽器
下面我們來看一下點擊事件和觸摸事件的監聽三要素具體是那部分:
由於點擊事件比較簡單,系統已經幫我們處理了,並沒有找到具體事件是哪個。
View.OnClickListener 單擊事件監聽器必須實現的接⼝
View.OnCreateContextMenuListener 創建上下⽂菜單事件
View.OnFocusChangeListener 焦點改變事件
View.OnKeyListener 按鍵事件監聽器
View.OnLongClickListener 長按事件監聽器
View.OnTouchListener 觸摸屏事件監聽器
⾸先,事件監聽機制中由事件源,事件,事件監聽器三類對象組成。
事件監聽器處理流程:
在此以OnClickListener單擊事件為例使用intent來實現頁面的跳轉
監聽事件處理是事件源與事件監聽器分開的而基於回調的事件處理UI組件不但是事件源,而且還是事件監聽器,通過組件的相關回調方法處理對應的事件。
Ⅰ. 自定義View類,繼承自需要的View UI類。ex :自定義 MyButton按鈕類 extends 基礎Button類
Ⅱ. 復寫回調函數。ex:public boolean onTouchEvent(MotionEvent event)
每一個事件回調方法都會返回一個boolean值,①.如果返回true:表示該事件已被處理,不再繼續向外擴散,②.如果返回false:表示事件繼續向外擴散
而說到基於回調就離不開監聽機制 。
幾乎所有基於回調的事件處理方法都有一個boolean類型的返回值,該返回值用於表示該處理方法是否能完全處理該事件。
如果處理事件的回調方法返回true,表明該處理方法已經完全處理改事件,該事件不會傳播出去。
如果處理事件的回調方法返回false,表明該處理方法並未完全處理該事件,該事件會傳播出去。
對於基於回調的時間傳播而言,某組件上所發生的事件不僅會激發該組件上的回調方法,也會觸發該組件所在Activity的回調方法——只要事件能傳播到該Activity。
這里是在模擬器里進行的測試,這里按下鍵盤(而不是點擊),會看到 logcat 中的輸出,如下:
View類實現了KeyEvent.Callback介面中的一系列回調函數,因此,基於回調的事件處理機制通過自定義View來實現,自定義View時重寫這些事件處理方法即可。
Handler是一個消息分發對象。
Handler是Android系統提供的一套用來更新UI的機制,也是一套消息處理機制,可以通過Handler發消息,也可以通過Handler處理消息。
在下面介紹Handler機制前,首先得了解以下幾個概念:
在子線程執行完耗時操作,當Handler發送消息時,將會調用 MessageQueue.enqueueMessage ,向消息隊列中添加消息。 當通過 Looper.loop 開啟循環後,會不斷地從消息池中讀取消息,即調用 MessageQueue.next , 然後調用目標Handler(即發送該消息的Handler)的 dispatchMessage 方法傳遞消息, 然後返回到Handler所在線程,目標Handler收到消息,調用 handleMessage 方法,接收消息,處理消息。
從上面可以看出,在子線程中創建Handler之前,要調用 Looper.prepare() 方法,Handler創建後,還要調用 Looper.loop() 方法。而前面我們在主線程創建Handler卻不要這兩個步驟,因為系統幫我們做了。
初始化Looper :
從上可以看出,不能重復創建Looper,每個線程只能創建一個。創建Looper,並保存在 ThreadLocal 。其中ThreadLocal是線程本地存儲區(Thread Local Storage,簡稱TLS),每個線程都有自己的私有的本地存儲區域,不同線程之間彼此不能訪問對方的TLS區域。
開啟Looper
發送消息 :
post方法:
send方法:
在子線程中,進行耗時操作,執行完操作後,發送消息,通知主線程更新UI。
本文講解了三個方面;Android事件機制;基於監聽、基於回調以及Handler消息處理。還有許多沒有講解到的知識點,我總結在了整理的一套Android進階筆記裡面;需要學習進階的同學可以前往獲取: Frame Work源碼解析手冊 、 Android核心技術進階手冊、實戰筆記、面試題綱資料
『貳』 Android-View的事件分發及攔截-父控制項和子控制項都處理觸摸事件的方式
比如接著上篇 Android-View的事件分發及攔截機制簡單流程先體驗再研究(場景?疑問? 具體?待續...) ,小白現在要實現就是子View和父ViewGroup都響應點擊事件。
1. 單純的都只是響應down事件
這個就很簡單了 - 直接子View的**public boolean **onTouchEvent(MotionEvent event) 裡面直接返回false就行了。也就是子控制項響應了一次down後,接下來就交給父ViewGroup了.(子View就啥幾把也幹不了了);
2. 響應down和up事件,move啥的
我們知道子View如果onTouch裡面返回了true,那麼將會處理後續的move,up事件。而不再交給上層父ViewGroup。那父ViewGroup就沒辦法在Touch裡面處理,所以我們只能放到dispatchTouchEvent或者onInterceptTouchEvent中處理這個down,up等事件:
比如dispatchTouchEvent中:
這樣的情況就是父ViewGroup先執行點擊事件,然後子View再執行。 如果您需要父ViewGroup晚點,可以延時執行啥的。
如果此時,子View的dispatchTouchEvent返回true - 表示攔截,不繼續了
那麼子View的所有的事件都不會響應了。其實也就是我們的一個事件先傳遞,touch再處理的樹形圖:
網路上拔個圖來
簡單記錄下下而已,繼續加深理解...這是上一篇的續,還是上一篇....嘖嘖....後面是官方文檔分析來著...
『叄』 Android View 事件分發機制
Android 事件機制包含系統啟動流程、輸入管理(InputManager)、系統服務和 UI 的通信(WindowManagerService + ViewRootImpl + Window)、事件分發等一系列的環節。
Android 系統中將輸入事件定義為 InputEvent,根據輸入事件的類型又分為了 KeyEvent(鍵盤事件) 和 MotionEvent(屏幕觸摸事件)。這些事件統一由系統輸入管理器 InputManager 進行分發。
在系統啟動的時候,SystemServer 會啟動 WindowManagerService,WMS 在啟動的時候通過 InputManager 來負責監控鍵盤消息。
InputManager 負責從硬體接收輸入事件,並將事件通過 ViewRootImpl 分發給當前激活的窗口處理,進而分發給 View。
Window 和 InputManagerService 之間通過 InputChannel 來通信,底層通過 socket 進行通信。
Android Touch 事件的基礎知識:
KeyEvent 對應了鍵盤的輸入事件;MotionEvent 就是手勢事件,滑鼠、筆、手指、軌跡球等相關輸入設備的事件都屬於 MotionEvent。
InputEvent 統一由 InputManager 進行分發,負責與硬體通信並接收輸入事件。
system_server 進程啟動時會創建 InputManagerService 服務。
system_server 進程啟動時同時會啟動 WMS,WMS 在啟動的時候就會通過 IMS 啟動 InputManager 來監控鍵盤消息。
App 端與服務端建立了雙向通信之後,InputManager 就能夠將產生的輸入事件從底層硬體分發過來,Android 提供了 InputEventReceiver 類,以接收分發這些消息:
Window 和 IMS 之間通過 InputChannel 通信。InputChannel 是一個 pipe,底層通過 socket 進行通信。在 ViewRootImpl.setView() 過程中注冊 InputChannel。
Android 事件傳遞機制是 先分發再處理 ,先由外部的 View 接收,然後依次傳遞給其內層的 View,再從最內層 View 反向依次向外層傳遞。
三個方法的關系如下:
分發事件:
應用了樹的 深度優先搜索演算法 (Depth-First-Search,簡稱 DFS 演算法),每個 ViewGroup 都持有一個 mFirstTouchTarget, 當接收到 ACTION_DOWN 時,通過遞歸遍歷找到 View 樹中真正對事件進行消費的 Child,並保存在 mFirstTouchTarget 屬性中,依此類推組成一個完整的分發鏈。在這之後,當接收到同一事件序列的其它事件如 ACTION_MOVE、ACTION_UP 時,則會跳過遞歸流程,將事件直接分發給下一級的 Child。
ViewGroup 分發事件的主要的任務是找一個 Target,並且用這個 Target 處理事件,主要邏輯如下 :
為什麼倒序查找 TouchTarget?
如果按添加順序遍歷,當 View 重疊時(FrameLayout),先添加的 View 總是能消費事件,而後添加的 View 不可能獲取到事件。
攔截事件:
[1] Android 事件分發機制的設計與實現
[2] Android 事件攔截機制的設計與實現
『肆』 請簡述什麼是android事件處理,並分析兩種android事件處理機制的實現過程和區別
Android提供了兩種方式的事件處理:基於回調的事件處理和基於監聽器的事件處理。
對於基於監聽器的事件處理而言,主要就是為Android界面組件綁定特定的事件監聽器;對於基於回調的事件處理而言,主要做法是重寫Android組件特定的回調函數,Android大部分界面組件都提供了事件響應的回調函數,我們主要重寫它們就行。
一 基於監聽器的事件處理
相比於基於回調的事件處理,這是更具「面向對象」性質的事件處理方式。在監聽器模型中,主要涉及三類對象:
1)事件源Event Source:產生事件的來源,通常是各種組件,如按鈕,窗口等。
2)事件Event:事件封裝了界面組件上發生的特定事件的具體信息,如果監聽器需要獲取界面組件上所發生事件的相關信息,一般通過事件Event對象來傳遞。
3)事件監聽器Event Listener:負責監聽事件源發生的事件,並對不同的事件做相應的處理。
基於監聽器的事件處理機制是一種委派式Delegation的事件處理方式,事件源將整個事件委託給事件監聽器,由監聽器對事件進行響應處理。這種處理方式將事件源和事件監聽器分離,有利於提供程序的可維護性。
舉例:
View類中的OnLongClickListener監聽器定義如下:(不需要傳遞事件)
[java] view plainprint?
public interface OnLongClickListener {
boolean onLongClick(View v);
}
public interface OnLongClickListener {
boolean onLongClick(View v);
}
View類中的OnLongClickListener監聽器定義如下:(需要傳遞事件MotionEvent)
[java] view plainprint?
public interface OnTouchListener {
boolean onTouch(View v, MotionEvent event);
}
public interface OnTouchListener {
boolean onTouch(View v, MotionEvent event);
}
二 基於回調的事件處理
相比基於監聽器的事件處理模型,基於回調的事件處理模型要簡單些,該模型中,事件源和事件監聽器是合一的,也就是說沒有獨立的事件監聽器存在。當用戶在GUI組件上觸發某事件時,由該組件自身特定的函數負責處理該事件。通常通過重寫Override組件類的事件處理函數實現事件的處理。
舉例:
View類實現了KeyEvent.Callback介面中的一系列回調函數,因此,基於回調的事件處理機制通過自定義View來實現,自定義View時重寫這些事件處理方法即可。
[java] view plainprint?
public interface Callback {
// 幾乎所有基於回調的事件處理函數都會返回一個boolean類型值,該返回值用於
// 標識該處理函數是否能完全處理該事件
// 返回true,表明該函數已完全處理該事件,該事件不會傳播出去
// 返回false,表明該函數未完全處理該事件,該事件會傳播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
public interface Callback {
// 幾乎所有基於回調的事件處理函數都會返回一個boolean類型值,該返回值用於
// 標識該處理函數是否能完全處理該事件
// 返回true,表明該函數已完全處理該事件,該事件不會傳播出去
// 返回false,表明該函數未完全處理該事件,該事件會傳播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
三 比對
基於監聽器的事件模型符合單一職責原則,事件源和事件監聽器分開實現;
Android的事件處理機制保證基於監聽器的事件處理會優先於基於回調的事件處理被觸發;
某些特定情況下,基於回調的事件處理機制會更好的提高程序的內聚性。
四 基於自定義監聽器的事件處理流程
在實際項目開發中,我們經常需要自定義監聽器來實現自定義業務流程的處理,而且一般都不是基於GUI界面作為事件源的。這里以常見的app自動更新為例進行說明,在自動更新過程中,會存在兩個狀態:下載中和下載完成,而我們的程序需要在這兩個狀態做不同的事情,「下載中」需要在UI界面上實時顯示軟體包下載的進度,「下載完成」後,取消進度條的顯示。這里進行一個模擬,重點在說明自定義監聽器的事件處理流程。
『伍』 Android ANR 機制
廣播的 ANR 處理相對簡單,主要是再次判斷是否超時、記錄日誌,記錄 ANR 次數等。然後就繼續調用 processNextBroadcast 函數,處理下一條廣播了。
ContentProvider 超時為 CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s
Activity 的 ANR 是相對最復雜的,也只有 Activity 中出現的 ANR 會彈出 ANR 提示框。
最終的表現形式是:彈出一個對話框,告訴用戶當前某個程序無響應,輸入一大堆與 ANR 相關的日誌,便於開發者解決問題。
InputDispatching:
Activity 最主要的功能之一是交互,為了方便交互,Android 中的 InputDispatcher 會發出操作事件,最終在 InputManagerService 中發出事件,通過 InputChannel,向 Activity 分發事件。交互事件必須得到響應,如果不能及時處理,IMS 就會報出 ANR,交給 AMS 去彈出 ANR 提示框。
KeyDispatching:
如果輸入是個 Key 事件,會從 IMS 進入 ActivityRecord.Token.keyDispatchingTimeOut,然後進入 AMS 處理,不同的是,在 ActivityRecord 中,會先截留一次 Key 的不響應,只有當 Key 連續第二次處理超時,才會彈出 ANR 提示框。
窗口焦點:
Activity 總是需要有一個當前窗口來響應事件的,但如果遲遲沒有當前窗口(獲得焦點),比如在 Activity 切換時,舊 Activity 已經 onPause,新的 Activity 一直沒有 onResume,持續超過 5 秒,就會 ANR。
App 的生命周期太慢,或 CPU 資源不足,或 WMS 異常,都可能導致窗口焦點。
1. 判斷是否有 focused 組件以及 focused Application:
這種一般是在應用啟動時觸發,比如啟動時間過長在這過程中觸發了 keyevent 或者 trackball moteionevent 就會出現。
對應於
2. 判斷前面的事件是否及時完成:
對應於
出現這種問題意味著主線程正在執行其他的事件但是比較耗時導致輸入事件無法及時處理。
InputDispatcher 超時是最常見的 ANR 類型,而且其類型也比較多。
當用戶觸摸屏幕或者按鍵操作,首次觸發的是硬體驅動,驅動收到事件後,將該相應事件寫入到輸入設備節點, 這便產生了最原生態的內核事件。接著,輸入系統取出原生態的事件,經過層層封裝後成為 KeyEvent 或者 MotionEvent ;最後,交付給相應的目標窗口(Window)來消費該輸入事件。可見,輸入系統在整個過程起到承上啟下的銜接作用。
Input 模塊的主要組成:
每一個應用進程都會有一個 SignalCatcher 線程,專門處理 SIGQUIT,來到 art/runtime/signal_catcher.cc :
當應用發生 ANR 之後,系統會收集許多進程,來 mp 堆棧,從而生成 ANR Trace 文件。收集的第一個,也是一定會被收集到的進程,就是發生 ANR 的進程。接著系統開始向這些應用進程發送 SIGQUIT 信號,應用進程收到 SIGQUIT 後開始 mp 堆棧。
[1] developer ANRs
[2] Android ANR 分析詳解
[3] 看完這篇 Android ANR 分析,就可以和面試官裝逼了!
[4] 微信 Android 團隊手把手教你高效監控 ANR
[5] Input 系統—ANR 原理分析 - Gityuan
[6] 徹底理解安卓應用無響應機制 - Gityuan
[7] 理解 Android ANR 的觸發原理 - Gityuan
『陸』 android的事件處理機制有兩種
1.基於監聽的事件處理機制,有一個關鍵就是事件注冊。 但是我們在實踐的時候並沒有自己手動的為某個視圖控制項注冊監聽器。
解答: 我們會經常用到 諸如 setOnclickListener(),OnTouchListener()方法等。 從字面意義理解,它為設置...監聽器。 但是,它 跟注冊還是頗有一些區別的。 我想注冊實踐監聽器,就是將它掛在在一個線程上,也就是說有一個事件監聽線程,那麼,有事件的視圖,就至少是雙線程的程序了。 不過很可惜,在去看set..Listener的源碼的時候,是看不到它在java源碼方面的具體實現的。 也就是說,要麼它依賴操作系統實現,要麼它依賴jni實現,並且,事件線程由jni管理。 換言之,實現注冊監聽是由ni實現的。
2.事件源的觸發流程:
解答: 學習過操作系統朋友應該知道,操作系統的很多操作都是通過中斷來完成。 同理,比如一個點擊事件,android手機硬體中,包括了一個觸摸屏的硬體,它分為內屏和外屏。 其中負責觸發屏幕點擊和觸摸中斷的為內屏。 內屏大概由五個層次構成,具體有什麼用不知道,反正我拆過~~~ 從內屏上,當有電容屏感應的時候,會接收到你觸摸的位置信息,甚至觸摸力度!!! 這個消息經由系統中斷(具有最高優先順序,應該是由最高優先順序的進程通知)發送給cpu,經由cpu通過進程間的消息機制傳遞給這個進程(當前正在用戶界面運行的進程,這時候只有一個),也就是這個程序運行的內存空間的某個點。(或者說通過廣播機制,將這個事件發送給所有的app也是有可能的)。
『柒』 Android 系統運行機制 【Looper】【Choreographer】篇
目錄:
1 MessageQueue next()
2 Vsync
3 Choreographer doFrame
4 input
系統是一個無限循環的模型, Android也不例外,進程被創建後就陷入了無限循環的狀態
系統運行最重要的兩個概念:輸入,輸出。
Android 中輸入 輸出 的往復循環都是在 looper 中消息機制驅動下完成的
looper 的循環中, messageQueue next 取消息進行處理, 處理輸入事件, 進行輸出, 完成和用戶交互
應用生命周期內會不斷 產生 message 到 messageQueue 中, 有: java層 也有 native層
其中最核心的方法就是 messageQueue 的 next 方法, 其中會先處理 java 層消息, 當 java 層沒有消息時候, 會執行 nativePollOnce 來處理 native 的消息 以及監聽 fd 各種事件
從硬體來看, 屏幕不會一直刷新, 屏幕的刷新只需要符合人眼的視覺停留機制
24Hz , 連續刷新每一幀, 人眼就會認為畫面是流暢的
所以我們只需要配合上這個頻率, 在需要更新 UI 的時候執行繪制操作
如何以這個頻率進行繪制每一幀: Android 的方案是 Vsync 信號驅動。
Vsync 信號的頻率就是 24Hz , 也就是每隔 16.6667 ms 發送一次 Vsync 信號提示系統合成一幀。
監聽屏幕刷新來發送 Vsync 信號的能力,應用層 是做不到的, 系統是通過 jni 回調到 Choreographer 中的 Vsync 監聽, 將這個重要信號從 native 傳遞到 java 層。
總體來說 輸入事件獲取 Vsync信號獲取 都是先由 native 捕獲事件 然後 jni 到 java 層實現業務邏輯
執行的是 messageQueue 中的關鍵方法: next
next 主要的邏輯分為: java 部分 和 native 部分
java 上主要是取java層的 messageQueue msg 執行, 無 msg 就 idleHandler
java層 無 msg 會執行 native 的 pollOnce@Looper
native looper 中 fd 監聽封裝為 requestQueue, epoll_wait 將 fd 中的事件和對應 request 封裝為 response 處理, 處理的時候會調用 fd 對應的 callback 的 handleEvent
native 層 pollOnce 主要做的事情是:
vsync 信號,輸入事件, 都是通過這樣的機制完成的。
epoll_wait 機制 拿到的 event , 都在 response pollOnce pollInner 處理了
這里的 dispatchVsync 從 native 回到 java 層
native:
java:
收到 Vsync 信號後, Choreographer 執行 doFrame
應用層重要的工作幾乎都在 doFrame 中
首先看下 doFrame 執行了什麼:
UI 線程的核心工作就在這幾個方法中:
上述執行 callback 的過程就對應了圖片中 依次處理 input animation traversal 這幾個關鍵過程
執行的周期是 16.6ms, 實際可能因為一些 delay 造成一些延遲、丟幀
input 事件的整體邏輯和 vsync 類似
native handleEvent ,在 NativeInputEventReceiver 中處理事件, 區分不同事件會通過 JNI
走到 java 層,WindowInputEventReceiver 然後進行分發消費
native :
java:
input事件的處理流程:
輸入event deliverInputEvent
deliver的 input 事件會來到 InputStage
InputStage 是一個責任鏈, 會分發消費這些 InputEvent
下面以滑動一下 recyclerView 為例子, 整體邏輯如下:
vsync 信號到來, 執行 doFrame,執行到 input 階段
touchEvent 消費, recyclerView layout 一些 ViewHolder
scroll 中 fill 結束,會執行 一個 recyclerView viewProperty 變化, 觸發了invalidate
invalidate 會走硬體加速, 一直到達 ViewRootImpl , 從而將 Traversal 的 callback post choreographer執行到 traversal 階段就會執行
ViewRootImpl 執行 performTraversal , 會根據目前是否需要重新layout , 然後執行layout, draw 等流程
整個 input 到 traversal 結束,硬體繪制後, sync 任務到 GPU , 然後合成一幀。
交給 SurfaceFlinger 來顯示。
SurfaceFlinger 是系統進程, 每一個應用進程是一個 client 端, 通過 IPC 機制,client 將圖像顯示工作交給 SurfaceFlinger
launch 一個 app:
『捌』 Android事件分發機制
Android中對視圖的Touch事件進行分發處理。
單手指操作:ACTION_DOWN -> ACTION_MOVE -> ACTION_UP
多手指操作:ACTION_DOWN -> ACTION_POINTER_DOWN -> ACTION_MOVE -> ACTION_POINTER_UP -> ACTION_UP.
(1) dispatchTouchEvent() :事件分發
(2) onInterceptTouchEvent() :事件攔截
(3) onTouchEvent() :事件處理
ViewGroup 的相關事件有三個:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。
View 的相關事件只有兩個:dispatchTouchEvent、onTouchEvent。
先分析ViewGroup的處理流程:首先得有個結構模型概念:ViewGroup和View組成了一棵樹形結構,最頂層為Activity的ViewGroup,下面有若乾的ViewGroup節點,每個節點之下又有若乾的ViewGroup節點或者View節點,依次類推。如圖:
點擊事件達到頂級 View(一般是一個 ViewGroup),會調用 ViewGroup 的 dispatchTouchEvent 方法,如果頂級 ViewGroup 攔截事件即 onInterceptTouchEvent 返回 true,則事件由 ViewGroup 處理,這時如果 ViewGroup 的 mOnTouchListener 被設置,則 onTouch 會被調用,否則 onTouchEvent 會被調用。也就是說如果都提供的話,onTouch 會屏蔽掉 onTouchEvent。在 onTouchEvent 中,如果設置了 mOnClickListenser,則 onClick 會被調用。如果頂級 ViewGroup 不攔截事件,則事件會傳遞給它所在的點擊事件鏈上的子 View,這時子 View 的 dispatchTouchEvent 會被調用。如此循環。
『玖』 Android應用處理MotionEvent的過程
應用對於Motion事件的處理比較復雜,不同類型的事件處理方式不同:
下面從應用UI線程的 Looper 開始分析
下面看 NativeInputEventReceiver 的 handleEvent()
對於 InputEventReceiver 的 dispatchInputEvent() 處理流程,在後文中分析。
下面看 InputConsumer 的 consume() 實現
對於Down事件等直接處理的事件,處理過程相對簡單,下面看Batch事件的處理過程。
下面從 NativeInputEventReceiver 的 consumeEvents() 開始分析。
對於Batch的首個Move事件,創建新的Batch,Move事件添加到Batch,然後循環讀取,通常返回WOULD_BLOCK(無事件可讀)。最終調用 WindowInputEventReceiver 的 () 。
下面看Vsync到來之前,Batch的後續Move事件的處理。
下面仍然從 NativeInputEventReceiver 的 consumeEvents() 開始分析
Batch的後續Move事件的處理相對簡單,只是將Move事件添加到Batch,下面分析Vsync信號到來後,Batch事件的處理。
下面從App收到Vsync信號調用 CALLBACK_INPUT 類型的回調 ConsumeBatchedInputRunnable 開始分析。
下面看 consumeEvents() 的處理
無論直接處理還是Batch處理,最終都通過 WindowInputEventReceiver 的 dispatchInputEvent() 處理,下面分析該過程。
下面從 WindowInputEventReceiver 的 OnInputEvent() 開始分析
下面看 deliverInputEvent() 的實現
下面看 EarlyPostImeInputStage 的對 MotionEvent 的處理
EarlyPostImeInputStage 處理完事件後,傳遞給 NativePostImeInputStage 處理, NativePostImeInputStage 的處理過程非常簡單,下面直接看 ViewPostImeInputStage 的處理。
至此,Motion事件已經傳遞給 Activity ,下面看Motion事件在 Activity 窗口內傳遞的過程。
下面從 Activity 的 dispatchTouchEvent() 開始分析
下面看 PhoneWindow 的 superDispatchTouchEvent() 的實現
下面分析 ViewGroup 的 dispatchTouchEvent() 實現
『拾』 Android RecyclerView點擊事件處理
Android新引入的RecyclerView用來代替ListView,但是RecyclerView處理點擊和長按事件卻沒有ListView方便
RecyclerView並沒有onClickListener和onLongClickListener方法
這樣我們就需要在Adapter中創建一個介面
同時在adapter中創建該介面屬性
然後在adapter implements OnClickListener,View.OnLongClickListener,在方法onCreateViewHolder中,
在方法onBindViewHolder中,設置
特別注意:這里胡itemView即接受點擊事件的view,並不需要在ViewHolder中顯式地初始化,當ViewHolder實例化之後,itemView也就自動實例化了。
這樣我們在adapter實現的onClick和onLongClick方法中,就可以
最後,在activity中實現介面
即可實現點擊事件