1、為什麽View 的繪制流程是從 ViewRootImpl 的performTraversals()方法開始的?
2、View 的invalidate方法是怎麽觸發到ViewRootImpl 的performTraversals()方法的。
在閱讀本文前,最好先了解window的添加過程,Android消息處理機制 和 View 的繪制流程。推薦先閱讀以下文章:
Android Window和WindowManager
Android-消息機制
Android View 的繪制流程
android 源碼註釋的意思是:ViewRootImpl是視圖層次結構的頂部,實現 View 和 WindowManager 之間所需的協議。是 WindowManager Global 的內部實現中重要的組成部分。
View 的繪制流程是從 ViewRootImpl 的performTraversals()方法開始的,那到底是哪裏調用了performTraversals()方法呢,下面我們分析壹下:
1.私有屬性的performTraversals()方法肯定是在內部調用起來的,經過搜索找到是doTraversal()方法調用了。
2.接著找到了,調用了doTraversal() 的TraversalRunnable 類
3.內部只有壹個地方實例化了TraversalRunnable 的實例mTraversalRunnable ,查到到兩個方法內都調用了mTraversalRunnable ,明顯 scheduleTraversals 是主動觸發這個 Runnable 。這就表明調用了scheduleTraversals ()函數的地方都主動觸發了view的刷新。
4.接著我們看壹下 mChoreographer.postCallback 做了什麽
可以看到,最後後走進了scheduleVsyncLocked()方法內。
5.mDisplayEventReceiver 的類 是 FrameDisplayEventReceiver,繼承自
DisplayEventReceiver 。
最後走到這裏就沒了,那麽這個方法是做了什麽呢,這個方法的註釋是這個意思:安排在下壹個顯示幀開始時傳送單個垂直同步脈沖。意思就是,調用了這個方法可以收到系統傳送過來的垂直同步脈沖信號。Android系統每隔16ms就會發送壹個VSYNC信號(VSYNC:vertical synchronization 垂直同步,幀同步),觸發對UI進行渲染。這個垂直同步信對於應用來說了,只有了訂閱了監聽,才能收到。而且是訂閱壹次,收到壹次。
6.既然是在這個類裏面訂閱垂直同步信號的,那回調也應該在這裏。於是找到了以下方法。
native code 調用到 onVsync,這個方法的註釋解釋如下:當接收到垂直同步脈沖時調用。接收者應該渲染壹個幀,然後調用 {@link scheduleVsync} 來安排下壹個垂直同步脈沖。
這個方法的具體實現在前面分析到的FrameDisplayEventReceiver 類裏面。
這裏可以看到,其實mHandler就是當前主線程的handler,當接收到onVsync信號的時候,將自己封裝到Message中,等到Looper處理,最後Looper處理消息的時候就會調用run方法,這裏是Handler的機制,不做解釋。
7.最後,如下圖調用所示,最終從mCallbackQueues取回之前添加的任務再執行run方法,也就是TraservalRunnable的run方法。
總結上面的分析,調用流程如下圖所示如下:
上面我們分析到只要調用了ViewRootImpl 的scheduleTraversals ()方法,最終就能調用了ViewRootImpl 的performTraversals()來開始繪制。那肯定是我們常調用的view刷新的接口,經過壹系列的方法調用,最終調用了ViewRootImpl 的scheduleTraversals ()方法。下面我們分析壹下常用的View 的 invalidate()接口是怎麽調用到了ViewRootImpl 的scheduleTraversals ()方法。
可以看出,invalidate有多個重載方法,但最終都會調用invalidateInternal方法,在這個方法內部,進行了壹系列的判斷,判斷View是否需要重繪,接著為該View設置標記位,然後把需要重繪的區域傳遞給父容器,即調用父容器的invalidateChild方法。
接著我們看ViewGroup#invalidateChild:
由於不斷向上調用父容器的方法,到最後會調用到ViewRootImpl的invalidateChildInParent方法,我們來看看它的源碼,ViewRootImpl#invalidateChildInParent:
最後調用了scheduleTraversals方法,觸發View的工作流程。至此,我們已經完整地分析了壹次調用View 的 invalidate()方法到觸發ViewRootImpl 的scheduleTraversals()方法。