終於放假啦,不用一直寫業務代碼了,也終於有點時間能夠整理整理筆記啦。
我在這裏先給你們拜個早年,恭祝你們新春快樂,吉祥安康啦!bash
FlutterFramework在Flutter層的項目入口是main
函數,默認生成的函數以下markdown
void main() {
runApp(MyApp());
}
複製代碼
runApp
是一個頂級函數,接受一個Widget
做爲rootWidget,這中間發生了什麼嘞?app
下文以圖片、源碼和文本解析的方式輔助學習。因爲內容較長,須要分爲幾個部分學習,而且序列圖、記錄點和實際運行順序有出入,下文排序主要是爲了可以由淺入深的鋪開runApp過程當中的知識點,幫助更好的理解。框架
main
函數,調用runApp(Widget)
函數///同main函數,runApp函數也是一個頂級函數
void main() {
runApp(MyApp());
}
複製代碼
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
複製代碼
ensureInitialized
函數會建立一個WidgetsFlutterBinding
的單例WidgetsBinding.instance
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
///確認WidgetsBinding.instance是否建立成功,flutter framework工做是是否完成
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
複製代碼
WidgetsFlutterBinding
經過mixIn語法繼承了7個BindingBase
子類,完成整個FlutterFramework層次的系統功能初始化。mixIn語法可參考個人這篇文章HelloDart-MixIn,土話記錄多繼承機制。abstract class BindingBase {
///會按照WidgetsFlutterBinding集成順序,以此調用每一個WidgetsFlutterBinding子類的
///initInstances和initServiceExtensions初始化函數。
BindingBase() {
developer.Timeline.startSync('Framework initialization');
assert(!_debugInitialized);
initInstances();
assert(_debugInitialized);
assert(!_debugServiceExtensionsRegistered);
initServiceExtensions();
assert(_debugServiceExtensionsRegistered);
developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
developer.Timeline.finishSync();
}
}
複製代碼
initInstances
和initServiceExtensions
該部分比較複雜,這裏先不學習了。簡單介紹一下BindingBase
系列子類的做用,下列內容我也是看註釋的,若有錯誤煩請指出
WidgetsFlutterBinding
:將FlutterFramework綁定到FlutterEngine上面GestureBinding
:綁定手勢系統。ServicesBinding
:主要做用與defaultBinaryMessenger
有關,用於和native通信相關。SchedulerBinding
:改類也繼承了ServicesBinding,主要用於調度幀渲染相關事件。PaintingBinding
:和painting庫綁定SemanticsBinding
:將語義層和FlutterEngine綁定起來。RendererBinding
:將渲染樹與FlutterEngine綁定起來。WidgetsBinding
:將Widget層與FlutterEngine綁定起來。 再次重申一下,上述內容只是看註釋得來的,若是錯漏,感謝指出初始化完WidgetsFlutterBinding.instance
及一堆系統Binding以後,便開始下一步操做了。異步
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
複製代碼
這裏使用級聯語法分別調用了scheduleAttachRootWidget(Widget)
和scheduleWarmUpFrame
函數,具體過程見下文ide
scheduleAttachRootWidget(app)
:經過級聯的方式,生成了WidgetsFlutterBinding.instance靜態實例並初始化一干系統功能以後,調用WidgetsFlutterBinding.scheduleAttachRootWidget(Widget root)
函數。void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
複製代碼
attachRootWidget(Widget)
:這裏異步調用WidgetsBinding.attachRootWidget(Widget rootWidget)
函數,只爲了儘快顯示Flutter項目的UI畫面。void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
複製代碼
scheduleWarmUpFrame()
:在上面第二點中,異步調用attachRootWidget
函數,只爲了儘快調用該函數,去顯示一個幀畫面。這個函數的詳細流程按下不表,等之後再學習。/// Schedule a frame to run as soon as possible, rather than waiting for
/// the engine to request a frame in response to a system "Vsync" signal.
void scheduleWarmUpFrame() {
...
}
複製代碼
RenderObjectToWidgetAdapter
:接着第2點,attachRootWidget函數做用就是將根Widget、根Element與根RenderObject三個跟對象綁定起來,並將惟一的BuildOwner對象引用做爲根對象的持有對象,經過繼承關係層層傳遞。看代碼///僞代碼
class WidgetsBinding ...{
void attachRootWidget(Widget rootWidget) {
//建立一個RenderObjectToWidgetAdapter對象,泛型T是RenderBox類型,
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',//renderView[註釋1]是鏈接到物理設備輸出層的渲染樹對象
child: rootWidget, //根Widget樹
).attachToRenderTree(buildOwner, renderViewElement);
}
}
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
/// Creates a bridge from a [RenderObject] to an [Element] tree.
///
/// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
RenderObjectToWidgetAdapter({
this.child,
this.container,
this.debugShortDescription,
}) : super(key: GlobalObjectKey(container));
/// The widget below this widget in the tree.
final Widget child;
/// The [RenderObject] that is the parent of the [Element] created by this widget.
final RenderObjectWithChildMixin<T> container;
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
@override
void updateRenderObject(BuildContext context, RenderObject renderObject) { }
///
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;
}
}
複製代碼
attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ])
:**這裏面關於element
的建立先忽略,在步驟3中學習。**該部分源碼在第四點中,咱們能夠看到RenderObjectToWidgetAdapter
會建立一個RenderObjectToWidgetElement
對象,做爲WidgetsBinding
中的_renderViewElement
對象。該_renderViewElement
對象也就是整個Element樹中的跟對象,其持有根Widget、根RenderView與BuildOwner對象。過程三主要分析RenderObjectToWidgetAdapter.attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ])
函數,期間還有一些BaseBinding內容摻雜進來,比較複雜。
函數
BuildOnwer.lockState(fn)
:post
///該函數中的_debugStateLocked 布爾值,每次調用該函數_debugStateLockLevel都會+1,
///callback運行完以後減1,若是在_debugStateLocked等於true期間調用setState就會拋出異常。
void lockState(void callback()) {
assert(callback != null);
assert(_debugStateLockLevel >= 0);
assert(() {
_debugStateLockLevel += 1;
return true;
}());
try {
callback();
} finally {
assert(() {
_debugStateLockLevel -= 1;
return true;
}());
}
assert(_debugStateLockLevel >= 0);
}
複製代碼
RenderObjectToWidgetAdapter.createElement()
:會建立一個RenderObjectToWidgetElement
對象。 學習
element.assignOwner(owner)
:將owner:BuildOnwer
賦值給element
,該owner:BuildOnwer
是整個Element樹的統一管理者。ui
element.mount(null, null)
:上圖忽略了BuildOwner.buildScope(Element context, [ VoidCallback callback ])
函數了,該函數做用在於將一個Element添加進去構建域中,並調用VoidCallback
函數做爲回調。
這裏要着重看下element對象一衆父類中的mount函數了
owner.buildScope(element, () {
element.mount(null, null);
});
class RenderObjectToWidgetElement ...{
@override
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
//這裏的child暫時爲null,忽略rebuild函數
_rebuild();
}
}
abstract class RootRenderObjectElement extends RenderObjectElement {
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
}
}
///RenderObjectElement類中,最重要的屬性,_renderObject
///是該根Element的RenderObject對象,該RenderObject類存儲
///了Element的位置信息,Skia是一個2D渲染框架,基於笛卡爾座標軸
///RenderObject也實現了基本的渲染功能。
abstract class RenderObjectElement extends Element {
/// The underlying [RenderObject] for this element.
@override
RenderObject get renderObject => _renderObject;
RenderObject _renderObject;
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
//這個回到步驟二中的RenderObjectToWidgetAdapter類,RendererBinding類初始化以後有個PipelineOwner
//對象,PipelineOwner對象中有個rootNode對象,就是該_renderObject了
_renderObject = widget.createRenderObject(this);
//這裏newSlot等於null,忽略
attachRenderObject(newSlot);
//將dirty置爲false,
_dirty = false;
}
}
class Element ...{
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
//一頓賦值
_parent = parent;
_slot = newSlot;
_depth = _parent != null ? _parent.depth + 1 : 1;
_active = true;
if (parent != null) // Only assign ownership if the parent is non-null
_owner = parent.owner;
if (widget.key is GlobalKey) {
final GlobalKey key = widget.key;
key._register(this);
}
_updateInheritance();
assert(() {
_debugLifecycleState = _ElementLifecycle.active;
return true;
}());
}
}
複製代碼
mount(Element parent, dynamic newSlot)
掛載函數在於將Element掛載到Element樹上去,若是有parent屬性就賦值,掛載以後Element的狀態變爲active。
SchedulerBinding.instance.ensureVisualUpdate()
:
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
複製代碼
SchedulerBinding.scheduleFrame()
:接着即是調用window.scheduleFrame()
函數了,這是一個native函數,純FlutterFramework部分線索就斷了。
void scheduleFrame() {
if (_hasScheduledFrame || !_framesEnabled)
return;
assert(() {
if (debugPrintScheduleFrameStacks)
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true;
}());
ensureFrameCallbacksRegistered();
window.scheduleFrame();
_hasScheduledFrame = true;
}
複製代碼
這裏作個runApp的簡單過程圖,隱藏了許多細節
main
函數。runApp(Widget)
函數傳入一個Widget做爲根Widget。Widget
只是一個配置類,不是實際的UI元素。runApp
經過WidgetsFlutterBinding
mixIn繼承一衆父類進行初始化。RendererBinding
父類中的renderView
對象,是實際的渲染對象。RenderObjectToWidgetAdapter
類,生成一個RenderObjectToWidgetElement<RenderBox>
類型的Element做爲根Element,並讓Widget、renderView和BuildOwner和根Element產生關係。SchedulerBinding.instance.ensureVisualUpdate()
函數,等待下一幀渲染。scheduleAttachRootWidget
是一個耗時操做,異步運行。runApp會優先調用scheduleWarmUpFrame()
渲染預熱幀。上述文章是筆者經過對Flutter1.12版本源碼進行學習的總結,若有錯漏,還煩請指出糾正,十分感謝。 另外本文只是runApp的源碼進行了跟蹤學習,Flutter究竟是如何渲染到Native設備、還有BaseBinding系列子類做用等,等以後篇學習筆記哈。