Flutter|setState流程分析
# setState流程分析
参考链接 Flutter setState流程分析 (opens new window)
# setState
void setState(VoidCallback fn) {
assert(() {
if (_debugLifecycleState == _StateLifecycle.defunct) {
// ...
// 不希望在stf的defunct阶段调用
}
if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
// ...
// ui还没在树上,不需要更新
}
return true;
}());
final Object? result = fn() as dynamic;
assert(() {
// ...
// 不希望fn的返回值是future
return true;
}());
// 标记为脏
_element!.markNeedsBuild();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# StatefulElement
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
...
_state._element = this;
...
_state._widget = widget;
assert(_state._debugLifecycleState == _StateLifecycle.created);
}
2
3
4
5
6
7
8
9
StatefulWidget对应的Element是StatefulElement,在StatefulElement中的构造方法中会通过StatefulWidget的createState创建State,同时将element本身设置给State的_element属性。而State也被保存在Element的_state属性中。
# markNeedsBuild
void markNeedsBuild() {
// ...
if (!_active)
return;
// ...
if (dirty)
return;
_dirty = true;
owner.scheduleBuildFor(this);
}
2
3
4
5
6
7
8
9
10
markNeedsBuild方法会调用owner的scheduleBuildFor方法,将该element标记为dirty,并且将element加入到一个全局的表示需要更新的Element列表中。owner是BuildOwner对象。
# scheduleBuildFor
void scheduleBuildFor(Element element) {
// ...
if (element._inDirtyList) {
// ...
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled();
}
_dirtyElements.add(element);
element._inDirtyList = true;
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
scheduleBuildFor 方法主要执行几个任务
- 判断element是否已经加入到_dirtyElements列表中,如果已经在列表中,就直接返回,不用再执行下面的操作。
- 判断_scheduledFlushDirtyElements是否为false,这个变量表示当前是否正在rebuild_dirtyElements中的元素。如果没有正在rebuild,并且onBuildScheduled回调不为空,就调用onBuildScheduled函数。
- 将element加入到_dirtyElements中,并且标记element的_inDirtyList为true,表示已经加入到脏元素列表。
通过搜索可以查到,BuildOwner是在WidgetBinding的initInstances方法中创建的,并且创建完成后设置了onBuildScheduled回调为WidgetsBinding的_handleBuildScheduled方法。
所以scheduleBuildFor方法又会调用到WidgetsBinding的_handleBuildScheduled方法。
# WidgetBinding _handleBuildScheduled
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
void initInstances() {
super.initInstances();
_instance = this;
...
// Initialization of [_buildOwner] has to be done after
// [super.initInstances] is called, as it requires [ServicesBinding] to
// properly setup the [defaultBinaryMessenger] instance.
_buildOwner = BuildOwner();
// 注意:onBuildScheduled 为 _handleBuildScheduled;
buildOwner.onBuildScheduled = _handleBuildScheduled;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
void _handleBuildScheduled() {
// If we're in the process of building dirty elements, then changes
// should not trigger a new frame.
...
ensureVisualUpdate();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
在_handleBuildScheduled调用ensureVisualUpdate,注意,ensureVisualUpdate并不是WidgetsBinding中的方法,而是SchedulerBinding中的方法,WidgetsBinding和SchedulerBinding都是mixin,被集成在WidgetsFlutterBinding类中,在应用启动执行runApp函数时会进行初始化。在dart中,一个类同时引入多个mixin,根据with的顺序,最右边的优先级更高。mixin有个线性化处理,如果右边的mixin重写了某一方法,并且在重写方法中调用了super.overrideMethod(),就会调用其左边的mixin的相应方法。
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
2
3
4
5
6
7
# scheduleFrame#ensureVisualUpdate
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
2
3
4
5
6
7
8
9
10
11
12
ensureVisualUpdate方法会通过SchedulerPhase枚举类判断当前的刷新状态。一共有五种状态 状态的转变流程为 transientCallbacks -> midFrameMicrotasks -> persistentCallbacks -> postFrameCallbacks -> idle 通过后面的分析,可以知道真正的刷新过程是在persistentCallbacks状态完成的。
所以,如果上次刷新已经完成(postFrameCallbacks或idle状态),就会调用scheduleFrame请求再次刷新。
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled)
return;
...
ensureFrameCallbacksRegistered();
window.scheduleFrame();
_hasScheduledFrame = true;
}
2
3
4
5
6
7
8
WidgetBinding的scheduleFrame会首先调用ensureFrameCallbacksRegistered方法确保window的回调函数以被注册。再调用window的scheduleFrame的方法。
void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
/// Requests that, at the next appropriate opportunity, the [onBeginFrame]
/// and [onDrawFrame] callbacks be invoked.
void scheduleFrame() => _scheduleFrame();
<Void Function()>(symbol: 'PlatformConfigurationNativeApi::ScheduleFrame')
external static void _scheduleFrame();
2
3
4
5
6
7
8
9
10
11
Window的scheduleFrame方法是个native方法,通过上面的注释,可以知道调用该方法后,onBeginFrame回调和onDrawFrame会被调用。这两个回调已经通过ensureFrameCallbacksRegistered设置为WidgetBinding的_handleBeginFrame和_handleDrawFrame方法。重点看下_handleDrawFrame方法。
void _handleDrawFrame() {
if (_ignoreNextEngineDrawFrame) {
_ignoreNextEngineDrawFrame = false;
return;
}
handleDrawFrame();
}
/// Called by the engine to produce a new frame.
///
/// This method is called immediately after [handleBeginFrame]. It calls all
/// the callbacks registered by [addPersistentFrameCallback], which typically
/// drive the rendering pipeline, and then calls the callbacks registered by
/// [addPostFrameCallback].
void handleDrawFrame() {
...
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
_schedulerPhase = SchedulerPhase.idle;
...
_currentFrameTimeStamp = null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
handleDrawFrame方法上面的注释已经说了该方法的作用,被引擎调用创建一个新的帧。这个方法流程也比较清晰,首先会循环执行_persistentCallbacks中的callback,这里的callback可以通过WidgetsBinding.instance.addPersistentFrameCallback(fn)注册;然后,再复制一份_postFrameCallbacks的拷贝,并将原_postFrameCallbacks列表清空,_postFrameCallbacks中保存重绘后执行的回调函数,并且只执行一次,可以通过WidgetsBinding.instance.addPostFrameCallback(fn)添加回调。执行完_persistentCallbacks和_postFrameCallbacks后,便将状态设置为SchedulerPhase.idle表示已经刷新过。
通过注释可以知道是通过addPersistentFrameCallback来驱动渲染的。通过搜索,可以看到在RendererBinding的initInstances方法中注册了persistentFrameCallback回调。
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
void initInstances() {
super.initInstances();
...
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
// 驱动渲染
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
在_handlePersistentFrameCallback回调函数中直接调用了drawFrame方法。
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
_mouseTracker.schedulePostFrameCheck();
}
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
需要注意的是,WidgetsBinding也实现了drawFrame,并且WidgetsBinding在被mixin到WidgetsFlutterBinding类时是在最右边,所以它的方法优先级最高。_handlePersistentFrameCallback中调用drawFrame方法时,会先调用WidgetsBinding中的drawFrame方法。
# WidgetsBinding#drawFrame
在WidgetsBinding的drawFrame方法中,先调用了buildOwner的buildScope方法,然后再调用了super.drawFrame(),通过super.drawFrame()可以调用到RendererBinding的drawFrame方法。先看buildOwner的buildScope方法。
void drawFrame() {
...
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame();
buildOwner.finalizeTree();
} finally {
assert(() {
debugBuildingDirtyElements = false;
return true;
}());
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# BuildOwner#buildScope
buildScope的核心逻辑就是,首先对_dirtyElements按照深度进行排序,再遍历_dirtyElements列表,调用其中元素的rebuild方法。rebuild方法定义在Element类中。
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
...
try {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
...
try {
_dirtyElements[index].rebuild();
} catch (e, stack) {
...
}
index += 1;
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {
// It is possible for previously dirty but inactive widgets to move right in the list.
// We therefore have to move the index left in the list to account for this.
// We don't know how many could have moved. However, we do know that the only possible
// change to the list is that nodes that were previously to the left of the index have
// now moved to be to the right of the right-most cleaned node, and we do know that
// all the clean nodes were to the left of the index. So we move the index left
// until just after the right-most clean node.
index -= 1;
}
}
}
...
} finally {
for (final Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
...
}
}
void rebuild() {
...
performRebuild();
...
}
void performRebuild();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
performRebuild是Element类中的抽象方法,各个子类会实现该方法。StateElement的父类是ComponentElement,先看ComponentElement的performRebuild方法.
void performRebuild() {
...
Widget built;
try {
..
built = build();
..
} catch (e, stack) {
_debugDoingBuild = false;
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(this));
},
),
);
} finally {
...
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
...
_child = updateChild(null, built, slot);
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
在这个方法中,直接调用build方法创建Widget,如果build方法产生异常,就会创建一个ErrorWidget,就是经常看到的红色警告界面。调用完build方法后,会再调用updateChild(_child, built, slot)更新子Widget。 StatelessElement和StatefulElement重写了build方法,分别调用了Widget和State的build方法。
// stl
Widget build() => (widget as StatelessWidget).build(this);
// stf
Widget build() => _state.build(this);
2
3
4
5
6
7
8
前面提到WidgetsBinding的drawFrame方法会通过super.drawFrame()调用到RendererBinding的drawFrame方法,再回头看RendererBinding的drawFrame方法。
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
2
3
4
5
6
7
8
9
10
11
12
RendererBinding的drawFrame方法,通过pipelineOwner对象重新layout和paint,已达到更新UI的效果。
# 总结
StatefulWidget通过setState方法将其对应的StatefulElement添加到BuildOwner的dirtyElements中,并触发一次刷新。在收到刷新回调后,遍历dirtyElements中的元素,执行rebuild操作,以更新显示状态。