從上一篇文章 Widget,Element,RenderObject樹的構建和更新流程 分析了Flutter中的節點的構建,從源碼出發,說明節點是如何一步步地往下構建,在文章也提到了WidgetsFlutterBinding,可是沒有細說。markdown
在這篇文章裏面,主要分析App的構建過程,分析從runApp之後發生了什麼事情。分析一下WidgetsFlutterBinding是什麼東西app
文章後方也有一個流程圖,能夠經過看流程圖瞭解app的啓動流程函數
咱們的main函數裏面,一般都會調用runApp(Widget app)方法,那這個方法裏面到底作了什麼呢?runApp的實現以下佈局
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()//初始化
..scheduleAttachRootWidget(app)//綁定根widget
..scheduleWarmUpFrame();//確保儘快第一幀
}
複製代碼
這個方法裏面調用了WidgetsFlutterBinding的三個方法,分別用於初始化WidgetsFlutterBinding,和綁定widget,並開始構建頁面。先分析一下這個WidgetsFlutterBinding是什麼東西。post
Flutter中的Binding概念我理解爲一個膠水層,負責鏈接framework和engine層,關係大體以下ui
runApp方法就是建立一個膠水層WidgetsFlutterBinding,並鏈接framework和engine層。this
WidgetsFlutterBinding是一個Binding的實例,也就是一條橋,負責鏈接Flutter的engine和framework的之間的事件交互。WidgetsFlutterBinding的定義以下spa
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
}
複製代碼
WidgetsFlutterBinding繼承了不少BindingBase。而後with了不少mixin類,並且這些mixin都是繼承於BindingBase這個類debug
BindingBase這個類的方法比較多,抽出來與本文相關的一些方法,以下code
ui.SingletonFlutterWindow get window => ui.window;
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
void initInstances();
void initServiceExtensions();
複製代碼
其中的這個window是SingletonFlutterWindow類型的,位於Flutter 的engine層。SingletonFlutterWindow就是咱們Flutter app加載的窗口,並且SingletonFlutterWindow是一個單例,它繼承於FlutterView。
FlutterView中有一個方法
PlatformDispatcher get platformDispatcher;
複製代碼
這個方法返回一個platformDispatcher,這個platformDispatcher是Flutter 的一個事件分發器,負責Flutter分發engine的事件,和傳遞事件給engine層。
SingletonFlutterWindow繼承自FlutterView。也就繼承了platformDispatcher。也就是說SingletonFlutterWindow是經過platformDispatcher去處理來自Flutter engine層的事件, platformDispatcher將flutter engine的事件分發到SingletonFlutterWindow,把SingletonFlutterWindow的事件傳給flutter engine。
注意一下,BindingBase裏面也有一個platformDispatcher,BindingBase和FlutterView中的platformDispatcher是同一個。
如今說回WidgetsFlutterBinding,當在runApp中調用了WidgetsFlutterBinding.ensureInitialized的時候,會返回一個instance.若是instance爲空,則調用默認的構造函數去建立一個instance。
默認構造函數在BindingBase的實現以下
BindingBase() {
...
initInstances();//實例化
...
initServiceExtensions();//註冊服務
...
}
複製代碼
由於WidgetsFlutterBinding最後with的是WidgetsBinding了,WidgetsBinding是繼承了BindingBase,ServicesBinding,SchedulerBinding,PaintingBinding,GestureBinding,RendererBinding,SemanticsBinding。因此這些mixin的initInstances和initServiceExtensions方法也會一併執行。WidgetsFlutterBinding和其with的mixin都會一同實例化並註冊服務。
其中各個mixin負責的部分以下
其中須要注意的是
binding層和SingletonFlutterWindow,WidgetsFlutterBinding作交互。其中WidgetsFlutterBinding中的mixin都負責某一塊的功能。
大體關係以下
WidgetsFlutterBinding實例化後會繼續調用scheduleAttachRootWidget方法。實現以下
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
}
複製代碼
在上一篇文章咱們說過attachRootWidget方法用因而爲根widget生成一個根Element.生成Element調用了attachToRenderTree方法並傳入了BuildOwner和Element. attachRootWidget的實現以下
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
element!.mount(null, null);
});
// This is most likely the first time the framework is ready to produce
// a frame. Ensure that we are asked for one.
SchedulerBinding.instance!.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
複製代碼
這裏的處理邏輯我在上一篇文章也講到過,具體關於節點樹的構建能夠參看上一篇文章。當element爲空時,會調用SchedulerBinding.instance!.ensureVisualUpdate()的方法。這一個方法也很重要,通過下面的過程
ensureVisualUpdate() -> scheduleFrame() -> ensureFrameCallbacksRegistered()
複製代碼
最終會調用ensureFrameCallbacksRegistered方法,ensureFrameCallbacksRegistered方法以下
void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
複製代碼
通過一些列的調用,最終會把SchedulerBinding中的_handleBeginFrame和_handleDrawFrame這兩個callback傳給WidgetsFlutterBinding類的window
set onBeginFrame(FrameCallback? callback) {
platformDispatcher.onBeginFrame = callback;
}
set onDrawFrame(VoidCallback? callback) {
platformDispatcher.onDrawFrame = callback;
複製代碼
能夠看到,window中的方法會把callback傳給platformDispatcher。 也就是說WidgetsFlutterBinding.scheduleAttachRootWidget這個方法通過一系列的方法調用後,最終會把SchedulerBinding這個mixin的_handleBeginFrame和_handleDrawFrame傳給platformDispatcher。
前面說到,platformDispatcher分發來自enginee的事件。而在這裏SingletonFlutterWindow把platformDispatcher的onBeginFrame和onDrawFrame這兩個事件交給SchedulerBinding處理。
當硬件發出VSync信號時,會調用platformDispatcher的onDrawFrame。從上面可知,實際上會調用SchedulerBinding中的_handleDrawFrame方法。_handleDrawFrame會調用handleDrawFrame方法。handleDrawFrame方法定義以下
void handleDrawFrame() {
...
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
...
複製代碼
其中的_postFrameCallbacks裏面存儲的是callback,做用是硬件每次發出VSync信號的時候都會調用。
這個方法裏面會取出_postFrameCallbacks裏面全部的callback,而後執行callback。這裏的_postFrameCallbacks是在RenderBinding這個mixin的initInstances方法中傳入的,以下
void initInstances() {
super.initInstances();
_instance = this;
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
...
addPersistentFrameCallback(_handlePersistentFrameCallback);
...
}
複製代碼
在RenderBinding的initInstances方法作了兩件事
由於在WidgetsBinding中實現了drawFrame方法,因此會調用WidgetsBinding中的drawFrame方法。 以下
void drawFrame() {
...
if (renderViewElement != null)
buildOwner!.buildScope(renderViewElement!);//構建更新子樹
super.drawFrame();
buildOwner!.finalizeTree();告訴buildOwner能夠完成更新了,能夠釋放掉_inactiveElements
...
複製代碼
這個方法會先調用buildOwnerOwner的buildScope方法去構建和更新節點樹。而後調用super.drawFrame()方法去調用RederBinding的drawFrame方法。以下
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();//刷新佈局
pipelineOwner.flushCompositingBits();//圖層更新
pipelineOwner.flushPaint();//刷新繪製
if (sendFramesToEngine) {
renderView.compositeFrame(); // 告訴GPU渲染renderView
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
複製代碼
這個方法會去調用去管理RenderObject樹生成新的UI繪製信息,交給renderView,並生成一個Scene給SingletonFlutterWindow,而後GPU去渲染新的界面
硬件每次發出VSync信號處理流程以下
關於圖片中的一些補充
這個方法最重要就是調用window.scheduleFrame()方法,從而調用platformDispatcher.scheduleFrame()方法,去通知engine層須要繪製。engine會根據狀況儘快地調用platformDispatcher的onDrawFrame方法。
順便一提的是,當咱們開發過程當中調用了setState()方法的時候,會調用BuildOwner的scheduleBuildFor方法,scheduleBuildFor方法中會調用onBuildScheduled這個callback,onBuildScheduled是由WigetsBinding這個mixin中傳入的。對應着WigetsBinding的_handleBuildScheduled這個方法。
_handleBuildScheduled這個方法最終也是會調用window.scheduleFrame()通知engine更新界面。
runApp這個方法總的來講就是作了如下的事情