Flutter系列學(xué)習(xí)筆記
- Flutter筆記——handleDrawFrame幀繪制系列之一(源碼學(xué)習(xí))
- Flutter筆記——runApp發(fā)生了什么(源碼學(xué)習(xí))
- Flutter筆記——State.setState發(fā)生了什么(源碼學(xué)習(xí))
- 用Dart寫的身份證號(hào)校驗(yàn)代碼(可用于flutter項(xiàng)目)
- HelloDart-構(gòu)造函數(shù)特性
- HelloDart-MixIn,土話記錄多繼承機(jī)制
- Flutter筆記——MethodChannel(Native&Flutter數(shù)據(jù)交互)
- Flutter筆記——FlutterActivity
前言
前兩篇文章Flutter筆記——runApp發(fā)生了什么(源碼學(xué)習(xí))和Flutter筆記——State.setState發(fā)生了什么學(xué)習(xí)了Flutter中runApp()、修改UI元素State.setState()過程。
這篇文章主要學(xué)習(xí)的是Flutter中實(shí)際渲染UI的過程。
1 BaseBinding
BaseBinding系列是FlutterFramework的核心類,學(xué)習(xí)Flutter的UI渲染過程會(huì)涉及到WidgetsBinding、RenderBinding、SchedulerBinding等。由于Dart的mixIn菱形繼承語法,該部分比較難搞明白,只能從局部入手,抽絲剝繭般的去學(xué)習(xí)理解整體流程。
1.1 handleDrawFrame
在我的Flutter筆記——runApp發(fā)生了什么(源碼學(xué)習(xí))文章中,了解到WidgetsFlutterBinding.scheduleWarmUpFrame()函數(shù)用于調(diào)度展示一個(gè)預(yù)熱幀。而WidgetsFlutterBinding.scheduleAttachRootWidget(Widget rootWidget)函數(shù)使用Timer包裹,作為一個(gè)異步執(zhí)行函數(shù),在它執(zhí)行完畢之時(shí)最終會(huì)調(diào)用WidgetsBinding.handleDrawFrame()函數(shù)繪制幀。
那么handleDrawFrame()函數(shù)到底發(fā)生了什么?
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;
}
}
}
首先學(xué)習(xí)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]
簡單理解下,該類主要作用就是調(diào)度幀渲染任務(wù),當(dāng)然也可以運(yùn)行非渲染任務(wù)。主要是瞬間渲染、持久渲染與渲染回調(diào)任務(wù)等,例如持久的幀渲染監(jiān)聽注冊(cè)WidgetsBinding.instance.addPersistentFrameCallback(callback)就是該類的作用了。
回到handleDrawFrame()函數(shù),這里面循環(huán)執(zhí)行SchedulerBinding._persistentCallbacks與SchedulerBinding._postFrameCallbacks的注冊(cè)回調(diào)之外,好像沒做其他事情哦?那么線索斷了嗎?

1.2 initInstances
這里吐槽下mixIn菱形繼承,這個(gè)語法特性真的香嗎?
這里把眼光回到BaseBinding系列的初始化函數(shù)中,我們可以在RendererBinding.initInstances()函數(shù)中,找到SchedulerBinding.addPersistentFrameCallback(FrameCallback callback)函數(shù)的調(diào)用,這意味著在RendererBinding.initInstances()初始化階段,已經(jīng)注冊(cè)了一個(gè)關(guān)鍵函數(shù),噔噔瞪,見下面源碼
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);
//重點(diǎn)
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
}
//重點(diǎn)
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
}
}
我們可以看到,在SchedulerBinding._persistentCallbacks已經(jīng)注冊(cè)了drawFrame函數(shù)回調(diào),到了這里handleDrawFrame渲染幀的線索又接上了,接著往下看。
1.3 drawFrame
drawFrame()函數(shù)有2處實(shí)現(xiàn)(有一處Test環(huán)境,忽略),并且都被WidgetsFlutterBinding繼承,這個(gè)mixIn真的香嗎?

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在每個(gè)handleDrawFrame函數(shù)中都會(huì)被調(diào)用,我們的WidgetsFlutterBinding繼承自RendererBinding和WidgetsBinding,見下圖的順序看看drawFrame到底發(fā)生了什么,再進(jìn)行源碼追蹤

過程比較復(fù)雜,源碼學(xué)習(xí)按照序列圖中的順序來
-
WidgetsBinding.drawFrame():該函數(shù)在每一次handleDrawFrame都會(huì)被調(diào)用,并且還會(huì)調(diào)用super.drawFrame函數(shù)///偽代碼 mixin WidgetsBinding ...{ ///忽略斷言和調(diào)試部分代碼 @override void drawFrame() { try { ///如果renderViewElement不為空,調(diào)用BuildOwner.buildScope函數(shù),生成WidgetTree更新域 if (renderViewElement != null){ buildOwner.buildScope(renderViewElement); } //調(diào)用RenderBinding.drawFrame函數(shù) 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是一個(gè)RenderObjectToWidgetElement<RenderBox>對(duì)象,在runApp(Widget app)函數(shù)中被初始化,不了解的請(qǐng)看我的這篇文章Flutter筆記——runApp發(fā)生了什么(源碼學(xué)習(xí))。
buildOwner.buildScope(renderViewElement)函數(shù)的作用是建立WidgetTree構(gòu)建的域。///刪除斷言和callback相關(guān)代碼 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,根據(jù)Element中的depth值,depth值是當(dāng)期Element所在樹的層次整數(shù)。每個(gè)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容器中的元素,調(diào)用它們的rebuild()函數(shù)。 -
element.rebuild():這里以ComponentElement作為示例,rebuild()函數(shù)源碼如下void rebuild() { ///刪除很多斷言和其他代碼 performRebuild(); } -
ComponentElement.performRebuild():在這里我們可以看到performRebuild()函數(shù)會(huì)調(diào)用Element中的build()函數(shù),這對(duì)于我們應(yīng)該是最熟悉的Flutter代碼之一了。這里面的built = build()有幾個(gè)繼承,StatefulWidget通過createState()函數(shù)生成State,再通過State的build():Widget函數(shù)生成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對(duì)象,這里面有三個(gè)參數(shù),第一個(gè)是之前的Widget對(duì)象,也就是類對(duì)象child。第二個(gè)是新生成的newWidget對(duì)象,由build()函數(shù)生成,第三個(gè)newSlot是父Element給與子Element的位置參數(shù),如果slot位置發(fā)生了變化,即使child與newWidget相同,也會(huì)重新渲染。@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):根據(jù)給定的Widget和newSlot生成一個(gè)Element,該方法通常由updateChild()函數(shù)直接調(diào)用。如果該Widget生成Element已經(jīng)存在或者存在相同的GlobalKey將會(huì)復(fù)用。該函數(shù)還會(huì)調(diào)用Widget.canUpdate(Widget oldWidget, Widget newWidget)來比較Widget對(duì)象是否相同。
該部分源碼較長,在之后文章看是否記錄學(xué)習(xí),這里知道其作用即可。- 如果newWidget的key是
GlobalKey,并且通過Element _retakeInactiveElement(GlobalKey key, Widget newWidget)能拿回來一個(gè)Element,那么在更新狀態(tài)與slot、配置之后便返回一個(gè)Element。 - 不能從key中拿回已有的Element,會(huì)調(diào)用
Element newChild = newWidget.createElement()生成一個(gè)新的newChild,并掛載它newChild.mount(this, newSlot)并返回。
- 如果newWidget的key是
-
super.drawFrame():也就是RenderBinding.drawFrame()函數(shù),該函數(shù)涉及知識(shí)點(diǎn)較多,下篇文章學(xué)習(xí)。它主要涉及到了RenderObject、Rect、PipelineOwner等知識(shí)點(diǎn)。 -
buildOwner.finalizeTree():調(diào)用該函數(shù)來完成元素構(gòu)建。
2 小結(jié)
- 本篇文章從預(yù)熱幀
WidgetsFlutterBinding.scheduleWarmUpFrame()函數(shù)入手,找到FlutterFramework渲染幀的過程函數(shù)handleDrawFrame(),再通過BaseBinding系列找到drawFrame()的持久監(jiān)聽與回調(diào)來學(xué)習(xí)幀繪制的部分內(nèi)容。 - 本文從
Element的create與update中,也找到了State.setState時(shí),有些UI元素沒有重繪的根本原因,也了解了key的作用。 -
BaseBinding中的WidgetsBinding、RenderBinding、SchedulerBinding等子類是FlutterFramework幀渲染的核心類。本文從drawFrame入手學(xué)習(xí)了部分內(nèi)容,另外BuildOwner全局管理類也要著重了解。 - 本文篇章有限,還有許多內(nèi)容沒有學(xué)習(xí)到,等下篇文章再著重學(xué)習(xí)
RenderBinding.drawFrame()的作用,之后再做一個(gè)階段性總結(jié)。
謝謝閱讀,如有錯(cuò)誤勞煩指出糾正,十分感謝,新春快樂哦!