Flutter渲染机制分析(二) —— Element
Element 调用 markNeedsBuild 后是如何刷新的
markNeedsBuild的描述
Marks the element as dirty and adds it to the global list of widgets to rebuild in the next frame.
将元素标记为脏元素,并将其添加到在下一帧中重建。,
说明: markNeedsBuild
所做的是只是将需要刷新的element
标记为了 “脏”element
, 在调用下一帧的时候才对“脏”element
进行重建
markNeedsBuild做了什么
void markNeedsBuild() {
assert(_lifecycleState != _ElementLifecycle.defunct);
if (_lifecycleState != _ElementLifecycle.active) {
return;
}
assert(owner != null);
assert(_lifecycleState == _ElementLifecycle.active);
if (dirty) {
return;
}
_dirty = true;
owner!.scheduleBuildFor(this);
}
markNeedsBuild
第一步就对Element生命周期做了判断,Element只有是active活跃的才能顺利通过校验, 这里对defunct 和 active 状态做了判断, 那 Element一共有几种状态呢:
enum _ElementLifecycle {
initial,
active,
inactive,
defunct,
}
可以看到 element 一共有四种状态,initial初始化、active活跃的、inactive不活跃的、defunct失效的.
Element 为 active
通过校验后, 将_dirty
标记为true,也就是“脏”的, 也就是说Element将在下一帧重建
最后一步执行owner!.schedulerBuildFor
传入当前element,现在就要弄清楚owner
是什么, schedulerBuidFor
又做了什么
BuildOwner是什么
/// The object that manages the lifecycle of this element.
@override
BuildOwner? get owner => _owner;
BuildOwner? _owner;
这是Element中对持有的owner
的描述: 管理此element生命周期的对象.既然是管理element用的,那我们需要弄清楚的无非三点: 创建,更改,销毁
下面我们就来看看Element中的owner
是何时创建,更改,销毁的吧
abstract class RootRenderObjectElement extends RenderObjectElement {
/// Set the owner of the element. The owner will be propagated to all the
/// descendants of this element.
///
/// The owner manages the dirty elements list.
///
/// The [WidgetsBinding] introduces the primary owner,
/// [WidgetsBinding.buildOwner], and assigns it to the widget tree in the call
/// to [runApp]. The binding is responsible for driving the build pipeline by
/// calling the build owner's [BuildOwner.buildScope] method. See
/// [WidgetsBinding.drawFrame].
void assignOwner(BuildOwner owner) {
_owner = owner;
}
}
abstract class Element extends DiagnosticableTree implements BuildContext {
void mount(Element? parent, Object? newSlot) {
if (parent != null) {
_owner = parent.owner;
}
}
}
在framework.dart
中我们找到了两处owner
赋值, Flutter框架分析 -- runApp初始化通过前置知识,我想大家一眼就能看出RootRenderObjectElement
这个类,它就是我们runApp
添加的根Element.
看到这里我们就可以确定如下两点:
- _owner在assignOwner第一次赋值
- _owner在mount中重新被复制为父owner
看到这里大家还是有疑问,这里只有_owner
的赋值与重新赋值,没看到是如何重建的啊.
了解runApp
初始化这个知识点的同学,可能一眼就能看出来BuildOwner
是什么,在哪里创建.
不了解也没关系.在assignOwner
上有一行关键注释 [WidgetsBinding.buildOwner], and assigns it to the widget tree in the call to [runApp].
buildOwner,并将其分配给[runApp
]调用中的小部件树。
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
}
}
在这里我们看到了BuildOwner
这个类的创建时机, 也就是在WidgetBinding
绑定类initInstances
中创建的.也就是在runApp
中创建的.
不了解initInstances
何时执行的, Flutter框架分析 -- runApp初始化可以去看这篇文章讲解,
里面也提到了,BuildOwner
是渲染流程中很重要的一个类,它管理着我们的Widget的重建.
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
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);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
在这几个方法中我们看到了, RootRenderObjectElement
子类RenderObjectToWidgetElement
的创建, 还有assignOwner
函数的调用传入buildOwner
,还是比较清晰明了的.
到这里BuildOwner
是什么我们就弄明白了,接下来就要看BuildOwner
的方法 scheduleBuildFor
具体做了什么.
BuildOwner做了什么
void scheduleBuildFor(Element element) {
if (element._inDirtyList) {
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled!();
}
_dirtyElements.add(element);
element._inDirtyList = true;
}
scheduleBuildFor
主要做了两件事把这个“脏”element
加入_dirtyElements
“脏”element
列表;执行onBuildScheduled
回调, 这个方法的回调可以翻上去看看,
在WidgetBinding中创建BuildOwner
后设置的回调.
下面我们来看看onBuildScheduled
的回调_handleBuildScheduled
做了什么
void _handleBuildScheduled() {
// If we're in the process of building dirty elements, then changes
// should not trigger a new frame.
ensureVisualUpdate();
}
/// This has no effect if [schedulerPhase] is
/// [SchedulerPhase.transientCallbacks] or [SchedulerPhase.midFrameMicrotasks]
/// (because a frame is already being prepared in that case), or
/// [SchedulerPhase.persistentCallbacks] (because a frame is actively being
/// rendered in that case). It will schedule a frame if the [schedulerPhase]
/// is [SchedulerPhase.idle] (in between frames) or
/// [SchedulerPhase.postFrameCallbacks] (after a frame).
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
可以看到,在SchedulerPhase
调度阶段为idel
或postFrameCallbacks
会执行scheduleFrame
调度一帧. 下面就来看看这几个状态分别是什么.
idle
: 没有正在正在执行的帧,也就是空闲阶段transientCallbacks
: 当前正在执行临时回调midFrameMicrotasks
: 在执行临时回调期间,安排的微任务persistentCallbacks
: 当前正在执行持久回调postFrameCallbacks
: 帧后回调,通常用于处理下一帧
一共五个状态,只有idle
和postFrameCallbakcs
是可以用来处理下一帧的,也就是执行scheduleFrame
调度一帧
ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance;
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled) {
return;
}
ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
@protected
void ensureFrameCallbacksRegistered() {
platformDispatcher.onBeginFrame ??= _handleBeginFrame;
platformDispatcher.onDrawFrame ??= _handleDrawFrame;
}
scheduleFrame
所做的也非常简单,执行ensureFrameCallbacksRegistered
确保帧回调已全部注册, 同时调用platformDispatcher.scheduleFrame
调度一帧.
/// Requests that, at the next appropriate opportunity, the [onBeginFrame] and
/// [onDrawFrame] callbacks be invoked.
void scheduleFrame() => _scheduleFrame();
@FfiNative<Void Function()>('PlatformConfigurationNativeApi::ScheduleFrame')
external static void _scheduleFrame();
可以看到,这两个就涉及到navite
更深层的东西了,不在我们的设计范围
Requests that, at the next appropriate opportunity, the [onBeginFrame] and [onDrawFrame] callbacks be invoked.
请求在下一个适当的时机,执行[onBeginFrame]和[onDrawFrame]回调。可以看到scheduleFrame
上的这句话.也就是说请求一帧后最终会回调这两个函数.
现在我们把目光放到onBeginFrame
开始帧和onDrawFrame
绘制帧, 从名字我们就可以知道,最先回调的是onBeginFrame
,绘制阶段才会回调onDrawFrame
.
下面就来看看这两个方法都做了些什么.
FrameCallbacks 帧回调
_handleBeginFrame
void _handleBeginFrame(Duration rawTimeStamp) {
...
handleBeginFrame(rawTimeStamp);
}
void handleBeginFrame(Duration? rawTimeStamp) {
_frameTimelineTask?.start('Frame');
_firstRawTimeStampInEpoch ??= rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
if (rawTimeStamp != null) {
_lastRawTimeStamp = rawTimeStamp;
}
assert(schedulerPhase == SchedulerPhase.idle);
_hasScheduledFrame = false;
try {
// TRANSIENT FRAME CALLBACKS
_frameTimelineTask?.start('Animate');
_schedulerPhase = SchedulerPhase.transientCallbacks;
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
_transientCallbacks = <int, _FrameCallbackEntry>{};
callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
if (!_removedIds.contains(id)) {
_invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);
}
});
_removedIds.clear();
} finally {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}
@pragma('vm:notify-debugger-on-exception')
void _invokeFrameCallback(FrameCallback callback, Duration timeStamp, [ StackTrace? callbackStack ]) {
callback(timeStamp);
}
}
上面的handleBeginFrame
主要做了四件事:
- 流水线进入
Frame
调度一帧阶段,记录第一帧时间, 与当前帧时间 - 帧渲染时间线进入动画
Animate
阶段,调度状态为transientCallbacks
执行临时回调 - 执行临时帧回调,传入当前帧时间戳
- 最后调度状态修改为
midFrameMicrotasks
, 处理瞬时回调期间安排的微任务执行
在处理完动画回调,微任务执行完后,接着就会回到onDrawFrame
,现在我们就来看看这个函数做了什么.
_handleDrawFrame
在handleDrawFrame
上有这么一段注释,This method is called immediately after [handleBeginFrame]
,在handleBeginFrame执行完后,立马执行此方法,所以不用疑惑此处为什么执行的此方法.
/// 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() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
_frameTimelineTask?.finish(); // end the "Animate" phase
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>.of(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks) {
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
}
} finally {
_schedulerPhase = SchedulerPhase.idle;
_frameTimelineTask?.finish(); // end the Frame
assert(() {
if (debugPrintEndFrameBanner) {
debugPrint('▀' * _debugBanner!.length);
}
_debugBanner = null;
return true;
}());
_currentFrameTimeStamp = null;
}
}
我们看到方法第一步就是结束动画Animate
阶段,进入持久回调阶段
/// Typically, this is the build/layout/paint pipeline. See
/// [WidgetsBinding.drawFrame] and [SchedulerBinding.handleDrawFrame].
persistentCallbacks,
persistentCallbacks
上有这么一段描述,也就是说这个阶段是我们的渲染流水线Pipeline
阶段,
会执行我们的构建Build
、布局layout
、绘制paint
.
现在我们来看看持久帧是在哪里注册的呢
mixin SchedulerBinding on BindingBase {
/// Persistent frame callbacks cannot be unregistered. Once registered, they
/// are called for every frame for the lifetime of the application.
void addPersistentFrameCallback(FrameCallback callback) {
_persistentCallbacks.add(callback);
}
}
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
addPersistentFrameCallback(_handlePersistentFrameCallback);
}
}
addPersistentFrameCallback
上有段描述,持久回调是不会被注销的,它在应用生命周期内为每个帧所调用
可以看到在RendererBinding
绑定初始化期间添加的addPersistentFrameCallback
持久帧回调,不清楚RenderBinding
初始化流程的查看这篇文章Flutter框架分析 -- runApp初始化.
现在的持久回调将进入RenderBinding
中的_handlePersistentFrameCallback
执行,下面我们就来看看这个方法都做了什么吧
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
_scheduleMouseTrackerUpdate();
}
@protected
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;
}
}
可以看到方法内执行了渲染流水线的布局、绘制、发送合成帧到GPU显示. 至于内部具体的执行流程,我们不在这里深入探究,留到下篇文章分析.
发送合成帧至GPU显示后,就该进入我们的下一阶段了, 此时调度状态进入
SchedulerPhase.postFrameCallbacks
帧后回调阶段
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.of(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks) {
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
}
当帧后回调执行完后
_schedulerPhase = SchedulerPhase.idle;
_frameTimelineTask?.finish(); // end the Frame
调度状态变为空闲,一帧的时间线结束.
总结
到这里从setState
到markNeedsBuild
的一整套刷新流程就分析完毕了.
本篇我们需要掌握以下几个知识点:
- 管理
Element
重建的BuildOwner
什么时候创建的,它又做了些什么 FrameCallback
两个回调_handleBeginFrame
和_handleDrawFrame
分别做了什么,又是什么时候创建的- 持久回调
_persistentCallbacks
是什么时候注册的,它又做了什么
弄清楚这三点后,我们setState整个流程脉络就已经比较清晰了.
本篇分析就到这里,下篇我们开始分析,在持久回调中执行的渲染流水线,都做了些什么.
转载自:https://juejin.cn/post/7205087410949898298