事件分發,真的一定從Activity開始嗎?
前言
很高興遇見你~
事件分發,android中一個老生常談的話題了,基本的流程我們也都知道是從Activity開始分發,但有一個關鍵問題是:事件是如何到達Activity的 ?
你以為我接下來要開始講原始碼、系統底層了?不不不,本文不講這些,我們要探究的是,一個觸摸資訊從系統底層產生之后,是如何一步步到達目標view的,
本文是筆者android觸摸事件系列文章的開篇,主要的內容是分析觸摸事件傳遞的路徑,不會糾結于原始碼與底層,而是把觸摸事件來源的大體流程呈現出來,便于對事件分發體系有個更加完整的理解,
管理單位:window
android的view管理是以window為單位的,每個window對應一個view樹,Window機制不僅管理著view的顯示,也負責view的事件分發,關于window的本質,可以閱讀筆者的另一篇文章window機制,研究事件分發的來源,需要從window機制入手,
所以,首先要了解一個概念:view樹,
我們的應用布局,一般是有多層viewGroup和view的嵌套,如下圖:
而他們對應的結構關系如下圖所示
此時,我們就可以稱該布局是以一個LinearLayout為根的一棵view樹,LinearLayout可以直接訪問FrameLayout和RelativeLayout,因為他們都是LinearLayout的子view,同樣的RelativeLayout可以直接訪問Button,
每一棵view樹都有一個根,叫做ViewRootImpl ,他負責管理這整一棵view樹的繪制、事件分發等,所以可以說,事件分發是從viewRootImpl開始的,
我們的應用界面一般會有多個view樹,我們的activity布局就是一個view樹、其他應用的懸浮窗也是一個view樹、dialog界面也是一個view樹、我們使用windowManager添加的view也是一個view樹等等,最簡單的view樹可以只有一個view,
android中view的繪制和事件分發,都是以view樹為單位,每一棵view樹,則為一個window ,系統服務WindowManagerService,管理界面的顯示就是以window為單位,也可以說是以view樹為單位,而view樹是由viewRootImpl來負責管理的,所以可以說,wms(WindowManagerService的簡寫)管理的是viewRootImpl,如下圖:
對上圖做個簡單解釋,
- wms是運行在系統服務行程的,負責管理所有應用的window,應用程式與wms的通信必須通過Binder進行跨行程通信,
- 每個viewRootImpl在wms中都有一個windowState對應,wms可以通過windowState找到對應的viewRootImpl進行管理,
了解window機制,跟事件分發有什么關系呢?我們要知道,window機制管理的,不僅是view的顯示邏輯,事件分發也是其中的一個重要部分,了解window機制的一個重要原因是:事件分發并不是由Activity驅動的,而是由系統服務驅動viewRootImpl來進行分發 ,甚至可以說,在框架層角度,和Activity沒有任何關系,這將有助于我們對事件分發的本質理解,
那么觸摸資訊是如何一步步到達viewRootImpl、viewRootImpl如何對觸摸資訊進行分發處理的呢,這是我們接下來要討論的,
觸摸資訊是如何到達viewRootImpl的?
我們都知道的是,在我們手指觸摸螢屏時,即產生了觸摸資訊,這個觸摸資訊由螢屏這個硬體產生,被系統底層驅動獲取,交給Android的輸入系統服務:InputManagerService,也就是IMS,
IMS會對這個觸摸資訊進行處理,通過WMS找到要分發的window,隨后發送給對應的viewRootImpl,所以發送觸摸資訊的并不是WMS,WMS提供的是window的相關資訊,
這一部分涉及到系統底層的邏輯,不是本文的重點,感興趣的讀者推薦閱讀gityuan博主的文章Input系統-事件處理全程序,這里不展開講解,大體的程序如下圖:
當viewRootImpl接收到觸摸資訊時,也正是應用程式行程事件分發的開始,
viewRootImpl是如何分發事件的?
前面我們講到,viewRootImpl管理一棵view樹,view樹的最外層是viewGroup, 而viewGroup繼承于view,因此整一棵view樹,從外部可以看做一個view,viewRootImpl接收到觸摸資訊之后,經過處理之后,封裝成MotionEvent物件發送給他所管理的view,由view自己進行分發,
前面我們講到,view樹的根節點可以是一個viewGroup,也可以是一個單獨的view,因此,這里的派發就會有兩種不同的方式:直接給view進行處理 or viewGroup進行事件分發,viewGroup繼承自view,view中有一個方法用于分發事件:dispatchTouchEvent ,子類可重寫該方法來實作自己的分發邏輯,ViewGroup重寫了該方法,
我們的應用布局界面或者dialog的布局界面,頂層的viewGroup為DecorView,因此會呼叫DecorView的 dispatchTouchEvent 方法進行分發,DecorView重寫了該方法,邏輯比較簡單,僅僅做了一個判斷:
DecorView.java api29
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
- 如果window callBack物件不為空,則呼叫callBack物件的分發方法進行分發
- 如果window callBack物件為空,則呼叫父類ViewGroup的事件分發方法進行分發
而Activity實作了Window.CallBack介面,并在創建布局的時候,把自己設定給了DecorView,因此在Activity的布局界面中,DecorView會把事件分發給Activity進行處理,同理,在Dialog的布局界面中,會分發給實作了callBack介面的Dialog,
而如果頂層的viewGroup不是DecorView,那么對呼叫對應view的dispatchTouchEvent方法進行分發,例如,頂層的view是一個Button,那么會直接呼叫Button的 dispatchTouchEvent 方法;如果頂層viewGroup子類沒有重寫 dispatchTouchEvent 方法,那么會直接呼叫ViewGroup默認的 dispatchTouchEvent 方法,
整體的流程如下圖:
- viewRootImpl會直接呼叫管理的view的
dispatchTouchEvent方法,根據具體的view的型別,呼叫具體的方法, - view樹的根view可能是一個view,也可能是一個viewGroup,view會直接處理事件,而viewGroup則會進行分發,
- DecorView重寫了
dispatchTouchEvent方法,會先判斷是否存在callBack,優先呼叫callBack的方法,也就是把事件傳遞給了Activity, - 其他的viewGroup子類會根據自身的邏輯進行事件分發,
因此,觸摸事件一定是從Activity開始的嗎?不是,Activity只是其中的一種情況,只有Activity自己負責的那一棵view樹,才一定會到達activity,而其他的window,則不會經過Activity,觸摸事件是從viewRootImpl開始,而不是Activity,
總結
最后我們對整個流程進行一次回顧:
- IMS從系統底層接收到事件之后,會從WMS中獲取window資訊,并將事件資訊發送給對應的viewRootImpl
- viewRootImpl接收到事件資訊,封裝成motionEvent物件后,發送給管理的view
- view會根據自身的型別,對事件進行分發還是自己處理
- 頂層viewGroup一般是DecorView,DecorView會根據自身callBack的情況,選擇呼叫callBack或者呼叫父類ViewGroup的方法
到這里,雖然觸摸事件的“去脈”我們還不清楚,但是他的“來龍”就已經非常清晰了,后續Activity、Dialog等callBack,viewGroup,其他頂層ViewGroup物件如何對觸摸事件進行處理,我將會在下一篇文章進行分析,
事件分發的來源遠沒有這么簡單,原始碼的細節有非常多的內容值得我們去學習,而本文只是把整體的流程抽了出來,這里對于有興趣讀者推薦一本書:《深入理解android卷Ⅲ》,
感謝閱讀,希望文章對你有幫助!
全文到此,原創不易,覺得有幫助可以點贊收藏評論轉發,
筆者才疏學淺,有任何想法歡迎評論區交流指正,
如需轉載請評論區或私信交流,另外歡迎光臨筆者的個人博客:傳送門
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/249778.html
標籤:Android
