前兩篇文章Flutter筆記——runApp發生了什麼(源碼學習)和Flutter筆記——State.setState發生了什麼學習了Flutter中runApp()
、修改UI元素State.setState()
過程。
這篇文章主要學習的是Flutter中實際渲染UI的過程。app
BaseBinding
系列是FlutterFramework的核心類,學習Flutter的UI渲染過程會涉及到WidgetsBinding
、RenderBinding
、SchedulerBinding
等。因爲Dart的mixIn菱形繼承語法,該部分比較難搞明白,只能從局部入手,抽絲剝繭般的去學習理解總體流程。異步
在個人Flutter筆記——runApp發生了什麼(源碼學習)文章中,瞭解到WidgetsFlutterBinding.scheduleWarmUpFrame()
函數用於調度展現一個預熱幀。而WidgetsFlutterBinding.scheduleAttachRootWidget(Widget rootWidget)
函數使用Timer包裹,做爲一個異步執行函數,在它執行完畢之時最終會調用WidgetsBinding.handleDrawFrame()
函數繪製幀。
那麼handleDrawFrame()
函數到底發生了什麼?ide
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { void handleDrawFrame() { assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks); Timeline.finishSync(); // end the "Animate" phase try { // PERSISTENT FRAME CALLBACKS _schedulerPhase = SchedulerPhase.persistentCallbacks; for (FrameCallback callback in _persistentCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp); // POST-FRAME CALLBACKS _schedulerPhase = SchedulerPhase.postFrameCallbacks; final List<FrameCallback> localPostFrameCallbacks = List<FrameCallback>.from(_postFrameCallbacks); _postFrameCallbacks.clear(); for (FrameCallback callback in localPostFrameCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp); } finally { _schedulerPhase = SchedulerPhase.idle; Timeline.finishSync(); // end the Frame assert(() { if (debugPrintEndFrameBanner) debugPrint('▀' * _debugBanner.length); _debugBanner = null; return true; }()); _currentFrameTimeStamp = null; } } }
首先學習WidgetsBinding
類,見註釋函數
Scheduler for running the following:
- Transient callbacks, triggered by the system's [Window.onBeginFrame] callback, for synchronizing the application's behavior to the system's display. For example, [Ticker]s and [AnimationController]s trigger from these. - Persistent callbacks, triggered by the system's [Window.onDrawFrame] callback, for updating the system's display after transient callbacks have executed. For example, the rendering layer uses this to drive its rendering pipeline. - Post-frame callbacks, which are run after persistent callbacks, just before returning from the [Window.onDrawFrame] callback. - Non-rendering tasks, to be run between frames. These are given a priority and are executed in priority order according to a [schedulingStrategy]
簡單理解下,該類主要做用就是調度幀渲染任務,固然也能夠運行非渲染任務。主要是瞬間渲染、持久渲染與渲染回調任務等,例如持久的幀渲染監聽註冊WidgetsBinding.instance.addPersistentFrameCallback(callback)
就是該類的做用了。
回到handleDrawFrame()
函數,這裏面循環執行SchedulerBinding._persistentCallbacks
與SchedulerBinding._postFrameCallbacks
的註冊回調以外,好像沒作其餘事情哦?那麼線索斷了嗎?
post
這裏吐槽下mixIn菱形繼承,這個語法特性真的香嗎?
這裏把眼光回到BaseBinding
系列的初始化函數中,咱們能夠在RendererBinding.initInstances()
函數中,找到SchedulerBinding.addPersistentFrameCallback(FrameCallback callback)
函數的調用,這意味着在RendererBinding.initInstances()
初始化階段,已經註冊了一個關鍵函數,噔噔瞪,見下面源碼學習
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { @override void initInstances() { super.initInstances(); _instance = this; _pipelineOwner = PipelineOwner( onNeedVisualUpdate: ensureVisualUpdate, onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, ); window ..onMetricsChanged = handleMetricsChanged ..onTextScaleFactorChanged = handleTextScaleFactorChanged ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged ..onSemanticsAction = _handleSemanticsAction; initRenderView(); _handleSemanticsEnabledChanged(); assert(renderView != null); //重點 addPersistentFrameCallback(_handlePersistentFrameCallback); initMouseTracker(); } //重點 void _handlePersistentFrameCallback(Duration timeStamp) { drawFrame(); } }
咱們能夠看到,在SchedulerBinding._persistentCallbacks
已經註冊了drawFrame
函數回調,到了這裏handleDrawFrame
渲染幀的線索又接上了,接着往下看。ui
drawFrame()
函數有2處實現(有一處Test環境,忽略),而且都被WidgetsFlutterBinding
繼承,這個mixIn真的香嗎?
this
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { void drawFrame() { assert(renderView != null); pipelineOwner.flushLayout(); pipelineOwner.flushCompositingBits(); pipelineOwner.flushPaint(); renderView.compositeFrame(); // this sends the bits to the GPU pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. } } mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { @override void drawFrame() { assert(!debugBuildingDirtyElements); assert(() { debugBuildingDirtyElements = true; return true; }()); if (_needToReportFirstFrame && _reportFirstFrame) { assert(!_firstFrameCompleter.isCompleted); TimingsCallback firstFrameCallback; firstFrameCallback = (List<FrameTiming> timings) { if (!kReleaseMode) { developer.Timeline.instantSync('Rasterized first useful frame'); developer.postEvent('Flutter.FirstFrame', <String, dynamic>{}); } SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback); _firstFrameCompleter.complete(); }; SchedulerBinding.instance.addTimingsCallback(firstFrameCallback); } try { if (renderViewElement != null) buildOwner.buildScope(renderViewElement); super.drawFrame(); buildOwner.finalizeTree(); } finally { assert(() { debugBuildingDirtyElements = false; return true; }()); } if (!kReleaseMode) { if (_needToReportFirstFrame && _reportFirstFrame) { developer.Timeline.instantSync('Widgets built first useful frame'); } } _needToReportFirstFrame = false; } }
在1.2中,咱們知道drawFrame
在每一個handleDrawFrame
函數中都會被調用,咱們的WidgetsFlutterBinding
繼承自RendererBinding
和WidgetsBinding
,見下圖的順序看看drawFrame
到底發生了什麼,再進行源碼追蹤
spa
過程比較複雜,源碼學習按照序列圖中的順序來debug
WidgetsBinding.drawFrame()
:該函數在每一次handleDrawFrame都會被調用,而且還會調用super.drawFrame
函數
///僞代碼 mixin WidgetsBinding ...{ ///忽略斷言和調試部分代碼 @override void drawFrame() { try { ///若是renderViewElement不爲空,調用BuildOwner.buildScope函數,生成WidgetTree更新域 if (renderViewElement != null){ buildOwner.buildScope(renderViewElement); } //調用RenderBinding.drawFrame函數 super.drawFrame(); // buildOwner.finalizeTree(); } finally { assert(() { debugBuildingDirtyElements = false; return true; }()); } if (!kReleaseMode) { if (_needToReportFirstFrame && _reportFirstFrame) { developer.Timeline.instantSync('Widgets built first useful frame'); } } _needToReportFirstFrame = false; } }
buildOwner.buildScope(renderViewElement)
:這裏的renderViewElement
是一個RenderObjectToWidgetElement<RenderBox>
對象,在runApp(Widget app)
函數中被初始化,不瞭解的請看個人這篇文章Flutter筆記——runApp發生了什麼(源碼學習)。 buildOwner.buildScope(renderViewElement)
函數的做用是創建WidgetTree構建的域。
///刪除斷言和callback相關代碼 void buildScope(Element context, [ VoidCallback callback ]) { Timeline.startSync('Build', arguments: timelineWhitelistArguments); try{ _dirtyElements.sort(Element._sort); _dirtyElementsNeedsResorting = false; int dirtyCount = _dirtyElements.length; int index = 0; while (index < dirtyCount) { try { _dirtyElements[index].rebuild(); } catch (e, stack) { _debugReportException( ErrorDescription('while rebuilding dirty elements'), e, stack, informationCollector: () sync* { yield DiagnosticsDebugCreator(DebugCreator(_dirtyElements[index])); yield _dirtyElements[index].describeElement('The element being rebuilt at the time was index $index of $dirtyCount'); }, ); } index += 1; if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) { _dirtyElements.sort(Element._sort); _dirtyElementsNeedsResorting = false; dirtyCount = _dirtyElements.length; while (index > 0 && _dirtyElements[index - 1].dirty) { index -= 1; } } } } finally { for (Element element in _dirtyElements) { element._inDirtyList = false; } _dirtyElements.clear(); _scheduledFlushDirtyElements = false; _dirtyElementsNeedsResorting = null; Timeline.finishSync(); } }
_dirtyElements.sort(Element._sort)
:排列Element,根據Element
中的depth
值,depth
值是當期Element
所在樹的層次整數。每一個Element
的depth
值都大於ParentElement
的depth
值
static int _sort(Element a, Element b) { if (a.depth < b.depth) return -1; if (b.depth < a.depth) return 1; if (b.dirty && !a.dirty) return -1; if (a.dirty && !b.dirty) return 1; return 0; }
_dirtyElements[index].rebuild()
:遍歷_dirtyElements容器中的元素,調用它們的rebuild()
函數。element.rebuild()
:這裏以ComponentElement
做爲示例,rebuild()
函數源碼以下
void rebuild() { ///刪除不少斷言和其餘代碼 performRebuild(); }
ComponentElement.performRebuild()
:在這裏咱們能夠看到performRebuild()
函數會調用Element中的build()
函數,這對於咱們應該是最熟悉的Flutter代碼之一了。這裏面的built = build()
有幾個繼承,StatefulWidget
經過createState()
函數生成State
,再經過State
的build():Widget
函數生成Widget。
@override void performRebuild() { ///刪除不少斷言和其餘代碼 Widget built; try { built = build(); debugWidgetBuilderValue(widget, built); } catch (e, stack) { built = ErrorWidget.builder( _debugReportException( ErrorDescription('building $this'), e, stack, informationCollector: () sync* { yield DiagnosticsDebugCreator(DebugCreator(this)); }, ), ); } finally { _dirty = false; } try { _child = updateChild(_child, built, slot); assert(_child != null); } catch (e, stack) { built = ErrorWidget.builder( _debugReportException( ErrorDescription('building $this'), e, stack, informationCollector: () sync* { yield DiagnosticsDebugCreator(DebugCreator(this)); }, ), ); _child = updateChild(null, built, slot); } }
updateChild(Element child, Widget newWidget, dynamic newSlot)
:更新Element
中的Widget
對象,這裏面有三個參數,第一個是以前的Widget
對象,也就是類對象child
。第二個是新生成的newWidget
對象,由build()
函數生成,第三個newSlot
是父Element給與子Element的位置參數,若是slot位置發生了變化,即便child
與newWidget
相同,也會從新渲染。
@protected Element updateChild(Element child, Widget newWidget, dynamic newSlot) { if (newWidget == null) { if (child != null) deactivateChild(child); return null; } if (child != null) { if (child.widget == newWidget) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); return child; } if (Widget.canUpdate(child.widget, newWidget)) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); child.update(newWidget); assert(child.widget == newWidget); assert(() { child.owner._debugElementWasRebuilt(child); return true; }()); return child; } deactivateChild(child); assert(child._parent == null); } return inflateWidget(newWidget, newSlot); }
Element inflateWidget(Widget newWidget, dynamic newSlot)
:根據給定的Widget
和newSlot
生成一個Element,該方法一般由updateChild()
函數直接調用。若是該Widget
生成Element
已經存在或者存在相同的GlobalKey
將會複用。該函數還會調用Widget.canUpdate(Widget oldWidget, Widget newWidget)
來比較Widget對象是否相同。
該部分源碼較長,在以後文章看是否記錄學習,這裏知道其做用便可。
GlobalKey
,而且經過Element _retakeInactiveElement(GlobalKey key, Widget newWidget)
能拿回來一個Element,那麼在更新狀態與slot、配置以後便返回一個Element
。Element newChild = newWidget.createElement()
生成一個新的newChild
,並掛載它newChild.mount(this, newSlot)
並返回。super.drawFrame()
:也就是RenderBinding.drawFrame()
函數,該函數涉及知識點較多,下篇文章學習。它主要涉及到了RenderObject
、Rect
、PipelineOwner
等知識點。buildOwner.finalizeTree()
:調用該函數來完成元素構建。WidgetsFlutterBinding.scheduleWarmUpFrame()
函數入手,找到FlutterFramework渲染幀的過程函數handleDrawFrame()
,再經過BaseBinding
系列找到drawFrame()
的持久監聽與回調來學習幀繪製的部份內容。Element
的create
與update
中,也找到了State.setState
時,有些UI元素沒有重繪的根本緣由,也瞭解了key的做用。BaseBinding
中的WidgetsBinding
、RenderBinding
、SchedulerBinding
等子類是FlutterFramework幀渲染的核心類。本文從drawFrame入手學習了部份內容,另外BuildOwner
全局管理類也要着重瞭解。RenderBinding.drawFrame()
的做用,以後再作一個階段性總結。謝謝閱讀,若有錯誤勞煩指出糾正,十分感謝,新春快樂哦!