在 Flutter 項目中,應用的啓動通常是從 main.dart 開始的,java
void main() => runApp(App()); 複製代碼
App 就是應用的主頁面,本質上是一個 Widget,換句話說,在 Flutter 中,萬物皆爲 Widget,它由開發者編寫,runApp 則負責將這個 Widget 運行起來,展現到手機界面上,還有一系列的初始化工做,爲以後的持續工做、用戶交互作準備。瞭解 flutter 應用的啓動過程,能夠從這個函數開始。node
void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..attachRootWidget(app) ..scheduleWarmUpFrame(); } 複製代碼
從這裏能夠判斷,runApp 大體有三個過程:ios
WidgetsFlutterBinding 自己沒有什麼功能,只不過它集成了諸多 binding 類c++
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { /// Returns an instance of the [WidgetsBinding], creating and /// initializing it if necessary. If one is created, it will be a /// [WidgetsFlutterBinding]. If one was previously initialized, then /// it will at least implement [WidgetsBinding]. /// /// You only need to call this method if you need the binding to be /// initialized before calling [runApp]. /// /// In the `flutter_test` framework, [testWidgets] initializes the /// binding instance to a [TestWidgetsFlutterBinding], not a /// [WidgetsFlutterBinding]. static WidgetsBinding ensureInitialized() { if (WidgetsBinding.instance == null) WidgetsFlutterBinding(); return WidgetsBinding.instance; } } 複製代碼
依次是 BindingBase、GestureBinding、ServicesBinding、SchedulerBinding、PaintingBinding、SemanticsBinding、RendererBinding、WidgetsBinding,這裏又涉及到 mixin 語法相關的知識,具體的這裏不闡述,這種結果致使的結果就是 WidgetsFlutterBinding 擁有以上幾個類全部的功能,而涉及到的函數重寫相關的,若是按優先級來算,那就是調用函數時,先從 WidgetsBinding 中找是否有實現,若是沒有,再從 RendererBinding 中找,以此類推。而後再是 super 的使用,WidgetsBinding 中調用 super 函數會先從 RenderBinding 中找,RenderBinding 調用到 super 會先從 SemanticsBinding 中找,以此類推。緩存
而後, WidgetsFlutterBinding 沒有構造函數,直接看從父類的,在 BindingBase 的構造函數中調用了 initInstances 這個函數,它在後面的每個 binding 中都有實現,因此按照 mixin 的邏輯,應該先調用 WidgetsBinding 的實現,而在這幾個 binding 中都有調用 super.initInstances ,因此它會按照從 WidgetsBinding 至 BindingBase 的順序依次調用。markdown
分別向 BuildOwner 註冊了 onBuildScheduled 函數,向 Window 註冊了 onLocaleChanged 和 onAccessibilityFeaturesChanged 函數,向 SystemChannels 註冊了 MethodCallHandler 和 MessageHandler 函數,後面兩個可以接收傳遞過來的路由消息和系統消息,如 popRoute、pushRoute、memoryPressure 等。cookie
RendererBinding 中主要是與渲染相關的功能,初始化時建立了 PipelineOwner 實例,這個類就是負責界面渲染的,以後的 layout、paint 等過程 PipelineOwner 都會承擔着不少責任,而後又在 Window 中註冊了一些函數,接着初始化 RenderView ,還有就是一個比較重要的函數,_handlePersistentFrameCallback,這個函數被調用以後就會執行一系列的繪製操做,將 Widget 樹繪製到屏幕中。app
還有就是初始化了 RenderView,並將其加入 _nodesNeedingLayout 和 _nodesNeedingPaint,最後調用 requestVisualUpdate 請求刷新視圖。async
renderView 的 set 函數會將其與 PipelineOwner 聯繫起來,調用其 attach 函數,RenderView 的父類 RenderObject 重寫了這個函數,將 RenderView 標記成須要 layout、compositingBitsUpdate、paint 和 semanticsUpdate。ide
內部保存有一個 AccessibilityFeatures 實例,從 Window 中取得,Window 中又是從 C++ 中傳遞過來的,AccessibilityFeatures 本質上就是一個 int 值,而後經過位運算獲得配置信息,如是否反轉顏色、是否禁止動畫、是否顯示粗體字等 Android、ios 中的系統配置信息。
PaintingBinding 能夠認爲有兩個功能,一個是提供了 ImageCache 類,能夠用於緩存圖片,另外一個是調用了 ShaderWarmUp 的 execute 函數,從意思上看是用於啓動 skia(底層圖形繪製庫)的着色器編譯,這一點多是有預先啓動以防使用到再去啓動會影響等待時間的意味?
SchedulerBinding 向 Window 註冊了兩個回調函數,onBeginFrame 和 onDrawFrame ,這兩個函數就是在幀到來以後的回調,包括 Widgets 樹的刷新和繪製、動畫的處理以及其餘用戶投放的任務,對於 Android 平臺來講,這兩個函數調用的時機就是 Choreographer 回調以後,另外從功能上來講,它們也是與 Android 中基本一致的。
而後註冊了生命週期的回調函數 _handleLifecycleMessage 。
向 Window 註冊了 onPlatformMessage 函數,後續的開發插件時就是經過這個函數分發,而後每一個插件得以接收消息。
GestureBinding 向 Window 註冊了 onPointerDataPacket 函數,這個函數就是原平生臺上接收到觸摸事件以後回調的,也是 flutter 中觸摸手勢的提供方,它會在接收到傳來的觸摸事件以後,將其分發出去。
BindingBase 中則沒有實際的功能。
attachRootWidget 的參數是 app ,也就是在這個函數中,將用戶自定義的 widget 掛載到 flutter 應用的 widget 樹中,並生成 element 等,爲下一步繪製作準備。
這裏首先建立了 RenderObjectToWidgetAdapter 對象,將 widget 做爲其 child ,renderView 爲 container ,renderView 是在 RendererBinding 初始化時建立的,也是整個渲染樹的根節點,而後調用 RenderObjectToWidgetAdapter 的 attachToRenderTree 函數,將會利用 widget 樹生成 element 樹。
在這個函數中先是建立了根 element ,而後調用 buildScope 和 mount 函數,mount 能夠看做是生成 element 樹,buildScope 會將全部標記爲 dirty 的 element 進行 rebuild,能夠刷新視圖。
buildScope 會先調用 callback ,在這裏的 callback 就是 mount ,而後處理 _dirtyElements,先對其按照深度排序,再來一個 while 循環,調用每個 dirty element 的 rebuild 函數,而且若是在處理的過程當中發現 _dirtyElements 的數量發生變化或者須要從新處理的,就會將其從新排序,而後找到第一個 dirty element 繼續處理,完了以後就清空 _dirtyElements。
RenderObjectToWidgetElement 的 mount 函數會調用 _rebuild,而後又會調用 updateChild 更新 child ,具體的更新策略就是:
第一次執行的時候,child 爲 null,因此應該直接調用 inflateWidget 去生成。
函數的參數爲 widget 和 slot,首先會判斷 widget 的 key 是否爲 GlobalKey,若是它是一個 GlobalKey,也就意味着這個 element 是可全局惟一的,會先從 GlobalKey 中查找是否有可複用,沒有現成的 element 時纔會調用 createElement 生成 element,而後接着調用這個 element 的 mount 函數,與以前的 root element 不一樣,這裏的 element 對應的類通常是 ComponentElement 的子類,而 ComponentElement 有本身實現的 mount ,在它實現的 mount 中會依次調用到 _firstBuild,rebuild 和 performRebuild,在 performRebuild 中,先是生成了一個 widget 對象,接着又調用 updateChild 生成了 widget 對應的 element,由此造成一個調用鏈,updateChild 中會繼續調用 inflateWidget,直到整個 widget 樹轉化爲 element 樹以後結束。
element 樹生成以後,繪製的基礎就有了,下一步就是調用 scheduleWarmUpFrame 函數將這樣一個系統啓動運行。scheduleWarmUpFrame 會調用 handleBeginFrame 和 handleDrawFrame 函數開啓 flutter 應用的第一幀,後續的幀序列將會由 flutter 自行生成,在 Android 中它會展轉調用到 Android 中的 Choreographer 生成下一幀,而後再經過 C++ 傳回 flutter 中,完成自給。
在 handleBeginFrame 中主要是執行 _transientCallbacks,即調用 SchedulerBinding:scheduleFrameCallback 函數添加的回調,好比 Ticker 中的 scheduleTick 就經過這個函數延遲調用 _tick 函數。
handleDrawFrame 與 handleBeginFrame 相似,只不過執行了 _persistentCallbacks 和 _postFrameCallbacks 兩類回調,前者爲固有回調,好比在 RendererBinding 初始化中添加的 _handlePersistentFrameCallback,後者爲用戶須要在繪製以後執行的回調。
綜上,在每一幀中須要處理三種回調,除了 _persistentCallbacks 每一幀都要調用以外,其餘的都是一次性的。
這個函數會調用 drawFrame 函數,drawFrame 在 WidgetsBinding 和 RendererBinding 中分別有實現,根據 mixin 機制,調用的應該是 WidgetsBinding 中的實現,其功能大體分爲三部分:
1 中以前有說到過,就是 rebuild 全部的 dirty element,3 中會以每個 dirty element 爲起點,調用 element 及其全部 child 的 unmount 函數。
drawFrame 這個函數完成了本地各 widget 的繪製,以及將其渲染到屏幕上的整個過程,共五個過程:
第四個是 RenderView 的函數,其餘都是 PipelineOwner 的函數,PipelineOwner 在 RendererBinding 的初始化過程當中被建立。
這個過程完成全部 widget 的佈局計算,經過調用全部須要從新佈局的結點的 _layoutWithoutResize 函數,這個函數又分爲三個部分調用:
1 過程肯定 widget 的大小和位置,2 過程將 widget _needsSemanticsUpdate 標記爲 true,並從這個節點上溯到邊界節點,而後將這個節點入到 PipelineOwner 的 _nodesNeedingSemantics 中,並調用 requestVisualUpdate 函數,requestVisualUpdate 中調用的 onNeedVisualUpdate 在 PipelineOwnder 初始化時傳入,這個函數會在空閒狀態或 postFrameCallbacks 狀態請求下一幀,總之,2 過程的標記會影響 flushSemantics 過程的執行。3 過程則會從 widget 上溯到邊界節點,將這個節點加入到 _nodesNeedingPaint 中,這個過程會影響 flushPaint 的執行。
更新節點的 composition bits。
遍歷全部須要繪製的節點,若是節點沒有掛載到 widget 樹中,則跳過,不然就調用 PaintingContext.repaintCompositedChild 函數,而後依次調用 _paintWithContext、paint 函數,paint 函數通常是 widget 自由實現,能夠經過 PaintingContext 取得 Canvas ,並在 Canvas 上繪製任何內容,其使用與 Android 中的 draw 方法無二。
將 compositing bits 發送到 GPU。
將 semantics 發送到操做系統。
關於刷新幀,能夠以 setState 爲例,看一下 flutter 中是如何從 Android 原生中獲取下一幀回調的。
開發過 flutter 都知道,當咱們須要改變視圖的時候不能直接操做 Widget 或 Element ,而是改變數據,經過數據刷新驅動 widget 樹刷新,生成新的 element 樹,從而顯示出新的界面,而更新數據須要放在 setState 中,不然就會只有數據的更新,而不會帶動視圖更新,因此從這一點能夠猜想,setState 中作了某些處理,以致最終調用到 drawFrame,從而刷新視圖,那麼下面就分析下從調用 setState 到刷新視圖的全過程。
setState 的參數是一個 callback,在 setState 中會先調用 callback ,而後接着調用 markNeedsBuild ,前者通常是開發者用於改變數據的,那麼就只能從 markNeedsBuild 入手。
這個函數會將 element 標記爲 dirty,而後調用 scheduleBuildFor 函數,它會調用 onBuildScheduled 函數,而後將 element 加入 _dirtyElements,同時標記 _inDirtyList 爲 true。
onBuildScheduled 是在 WidgetsBinding 初始化時註冊到 BuildOwner 中的,對應的回調函數爲 _handleBuildScheduled,這個函數會調用 ensureVisualUpdate,而 ensureVisualUpdate 又會根據當前的 SchedulerPhase 值選擇性操做,只有在 idle 和 postFrameCallbacks 狀態時纔會調用 scheduleFrame ,其餘狀態,好比 transientCallbacks、midFrameMicrotasks、persistentCallbacks 等,都被看做是一幀正在進行中,而目前對 element 樹的改動仍是會被 drawFrame 處理到的,視圖會按照預期的刷新,因此不必再去請求新的幀。
scheduleFrame 會調用 Window 的 scheduleFrame 函數,這是一個 native 函數,會進入到 C++ 中執行,此舉就是爲了經過 C++ 繼而轉到 Android 代碼中執行,這個函數會在 C++ 中依次調用 window、runtime_controller、engine 和 animator 中對應的函數,最終調用 Animator::AwaitVSyn 。
void Animator::AwaitVSync() { waiter_->AsyncWaitForVsync( [self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time, fml::TimePoint frame_target_time) { if (self) { if (self->CanReuseLastLayerTree()) { self->DrawLastLayerTree(); } else { self->BeginFrame(frame_start_time, frame_target_time); } } }); delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_); } 複製代碼
在這個函數中調用了 VsyncWaiter::AsyncWaitForVsync 函數,並傳遞了一個 callback 函數,裏面會調用 BeginFrame,由此可知,這個 callback 就是在新的一幀到來時的回調函數,接着再看 VsyncWaiter 中的,它先將 callback 保存在成員變量中,而後調用了 AwaitVSync,以 Android 平臺爲例,這個函數將會被它的子類 VsyncWaiterAndroid 實現,這個實現利用 jni 調用 java 中的方法 FlutterJNI.asyncWaitForVsync。
在 java 中這個方法會調用 AsyncWaitForVsyncDelegate.asyncWaitForVsync 方法,這是一個接口,其實現以下:
private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate = new FlutterJNI.AsyncWaitForVsyncDelegate() { @Override public void asyncWaitForVsync(long cookie) { Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { float fps = windowManager.getDefaultDisplay().getRefreshRate(); long refreshPeriodNanos = (long) (1000000000.0 / fps); FlutterJNI.nativeOnVsync(frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie); } }); } }; 複製代碼
由此就能夠找到 Choreographer 與 flutter 之間的聯繫,flutter 調用的 requestFrame 最終會向 Choreographer 註冊一個回調,而後等待 Choreographer 下一幀的到來,再去執行 FlutterJNI.nativeOnVsync,可想而之,這個過程是一個從 java 調用到 flutter 的過程,與上面的相反。
這是一個 native 函數,會映射到調用 VsyncWaiterAndroid::OnNativeVsync 函數,VsyncWaiterAndroid 本質上看就是一個 jni 的接口類,它負責從 C++ 中調用 java 方法,也負責被 java 方法調用,再傳遞給 Animator。在它的 OnNativeVsync 中會依次調用 ConsumePendingCallback、FireCallback,在 FireCallback 中會先拿到以前傳進來的 callback ,而後就是在 UI 線程中調用它,也就是 Animator::AwaitVSync 中聲明的 callback 函數,因此,下一步就是調用 BeginFrame。
這個函數的主要實現就是調用 OnAnimatorBeginFrame,附加的還有如判斷當前管道中是有可用的 producer_continuation,以及在最後調用 OnAnimatorNotifyIdle。
Animator 的 delegate 是 Shell,Shell 會轉而調用 Engine 的 BeginFrame,Engine 再調用 RuntimeController 的 BeginFrame,而後是 Window 的 BeginFrame,Window 就是 C++ 中用於與 dart 交互的中間類,它負責從 C++ 中調用 dart 函數和 dart 對 C++ 函數的調用,在這裏,它會經過 DartInvokeField 前後調用 dart 中的 _beginFrame 和 _drawFrame。
在 dart 中 hooks.dart 負責接收 C++ 層的調用,好比 _beginFrame 函數:
@pragma('vm:entry-point') void _beginFrame(int microseconds) { _invoke1<Duration>(window.onBeginFrame, window._onBeginFrameZone, Duration(microseconds: microseconds)); } 複製代碼
它再去調用 window 中的函數,而 window 中的 onBeginFrame、onDrawFrame 等函數都是在以前初始化 SchedulerBinding 時就註冊過的,就是在以前 scheduleWarmUpFrame 也會調用的 handleBeginFrame 和 handleDrawFrame。
由此,每當 flutter 中須要刷新視圖時,就經過請求幀、回調、處理幀這樣一個過程完成。