事件分發,真的必定從Activity開始嗎?java
很高興碰見你~android
事件分發,android中一個老生常談的話題了。基本的流程咱們也都知道是從Activity開始分發,但有一個關鍵問題是:事件是如何到達Activity的 ?git
你覺得我接下來要開始講源碼、系統底層了?不不不,本文不講這些。咱們要探究的是,一個觸摸信息從系統底層產生以後,是如何一步步到達目標view的。api
本文是筆者android觸摸事件系列文章的開篇,主要的內容是分析觸摸事件傳遞的路徑。不會糾結於源碼與底層,而是把觸摸事件來源的大致流程呈現出來,便於對事件分發體系有個更加完整的理解。框架
android的view管理是以window爲單位的,每一個window對應一個view樹。Window機制不只管理着view的顯示,也負責view的事件分發。關於window的本質,能夠閱讀筆者的另外一篇文章window機制。研究事件分發的來源,須要從window機制入手。佈局
因此,首先要了解一個概念:view樹。post
咱們的應用佈局,通常是有多層viewGroup和view的嵌套,以下圖:學習
而他們對應的結構關係以下圖所示spa
此時,咱們就能夠稱該佈局是以一個LinearLayout爲根的一棵view樹。LinearLayout能夠直接訪問FrameLayout和RelativeLayout,由於他們都是LinearLayout的子view,一樣的RelativeLayout能夠直接訪問Button。3d
每一棵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。以下圖:
對上圖作個簡單解釋。
瞭解window機制,跟事件分發有什麼關係呢?咱們要知道,window機制管理的,不只是view的顯示邏輯,事件分發也是其中的一個重要部分。瞭解window機制的一個重要緣由是:事件分發並非由Activity驅動的,而是由系統服務驅動viewRootImpl來進行分發 ,甚至能夠說,在框架層角度,和Activity沒有任何關係。這將有助於咱們對事件分發的本質理解。
那麼觸摸信息是如何一步步到達viewRootImpl、viewRootImpl如何對觸摸信息進行分發處理的呢,這是咱們接下來要討論的。
咱們都知道的是,在咱們手指觸摸屏幕時,即產生了觸摸信息。這個觸摸信息由屏幕這個硬件產生,被系統底層驅動獲取,交給Android的輸入系統服務:InputManagerService,也就是IMS。
IMS會對這個觸摸信息進行處理,經過WMS找到要分發的window,隨後發送給對應的viewRootImpl。因此發送觸摸信息的並非WMS,WMS提供的是window的相關信息。
這一部分涉及到系統底層的邏輯,不是本文的重點,感興趣的讀者推薦閱讀gityuan博主的文章Input系統-事件處理全過程。這裏不展開講解。大致的過程以下圖:
當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); }
而Activity實現了Window.CallBack接口,並在建立佈局的時候,把本身設置給了DecorView,所以在Activity的佈局界面中,DecorView會把事件分發給Activity進行處理。同理,在Dialog的佈局界面中,會分發給實現了callBack接口的Dialog。
而若是頂層的viewGroup不是DecorView,那麼對調用對應view的dispatchTouchEvent
方法進行分發。例如,頂層的view是一個Button,那麼會直接調用Button的 dispatchTouchEvent
方法;若是頂層viewGroup子類沒有重寫 dispatchTouchEvent
方法,那麼會直接調用ViewGroup默認的 dispatchTouchEvent
方法。
總體的流程以下圖:
dispatchTouchEvent
方法,根據具體的view的類型,調用具體的方法。dispatchTouchEvent
方法,會先判斷是否存在callBack,優先調用callBack的方法,也就是把事件傳遞給了Activity。所以,觸摸事件必定是從Activity開始的嗎?不是,Activity只是其中的一種狀況,只有Activity本身負責的那一棵view樹,才必定會到達activity,而其餘的window,則不會通過Activity。觸摸事件是從viewRootImpl開始,而不是Activity。
最後咱們對整個流程進行一次回顧:
到這裏,雖然觸摸事件的「去脈」咱們還不清楚,可是他的「來龍」就已經很是清晰了。後續Activity、Dialog等callBack,viewGroup,其餘頂層ViewGroup對象如何對觸摸事件進行處理,我將會在下一篇文章進行分析。
事件分發的來源遠沒有這麼簡單,源碼的細節有很是多的內容值得咱們去學習,而本文只是把總體的流程抽了出來。這裏對於有興趣讀者推薦一本書:《深刻理解android卷Ⅲ》。
感謝閱讀,但願文章對你有幫助!
全文到此,原創不易,以爲有幫助能夠點贊收藏評論轉發。
筆者才疏學淺,有任何想法歡迎評論區交流指正。
如需轉載請評論區或私信交流。另外歡迎光臨筆者的我的博客:傳送門