likes
comments
collection
share

Flutter渲染机制分析(二) —— Element

作者站长头像
站长
· 阅读数 13

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调度阶段为idelpostFrameCallbacks会执行scheduleFrame调度一帧. 下面就来看看这几个状态分别是什么.

  • idle: 没有正在正在执行的帧,也就是空闲阶段
  • transientCallbacks: 当前正在执行临时回调
  • midFrameMicrotasks: 在执行临时回调期间,安排的微任务
  • persistentCallbacks: 当前正在执行持久回调
  • postFrameCallbacks: 帧后回调,通常用于处理下一帧

一共五个状态,只有idlepostFrameCallbakcs是可以用来处理下一帧的,也就是执行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

调度状态变为空闲,一帧的时间线结束.

总结

到这里从setStatemarkNeedsBuild的一整套刷新流程就分析完毕了. 本篇我们需要掌握以下几个知识点:

  • 管理Element重建的BuildOwner什么时候创建的,它又做了些什么
  • FrameCallback两个回调_handleBeginFrame_handleDrawFrame分别做了什么,又是什么时候创建的
  • 持久回调_persistentCallbacks是什么时候注册的,它又做了什么

弄清楚这三点后,我们setState整个流程脉络就已经比较清晰了.

本篇分析就到这里,下篇我们开始分析,在持久回调中执行的渲染流水线,都做了些什么.