Flutter 页面更新流程剖析

文章目录

  • Flutter页面更新流程剖析
    • 更新流程
    • 渲染过程
    • 视频课程

博主相关文章列表
Flutter 框架实现原理
Flutter 框架层启动源码剖析
Flutter 页面更新流程剖析
Flutter 事件处理源码剖析
Flutter 路由源码剖析
Flutter 安卓平台源码剖析(一)
Flutter 自定义控件之RenderObject

Flutter页面更新流程剖析

更新流程

当我们需要更新页面时,会调用setState方法,这里我们就以之为突破口,研究一下页面更新的流程。

flutter\lib\src\widgets\framework.dart

/// [State]

void setState(VoidCallback fn) {
   final dynamic result = fn() as dynamic;
  _element.markNeedsBuild();
}

删除断言后,其实只有两行代码,首先是执行了传入的闭包,接着调用markNeedsBuild()方法

/// [Element]

void markNeedsBuild() {
  if (!_active)
    return;
  if (dirty)
    return;
  // 将当前的Element标记为_dirty,表示需要重建
  _dirty = true;
  // 将当前元素添加到 dirty 元素列表中,这样当[WidgetsBinding.drawFrame]调用[buildScope]时它就会被重建
  owner.scheduleBuildFor(this);
}

这里调用了BuildOwner中的scheduleBuildFor方法

/// [BuildOwner]

void scheduleBuildFor(Element element) {
  if (element._inDirtyList) {
    _dirtyElementsNeedsResorting = true;
    return;
  }
  if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
    _scheduledFlushDirtyElements = true;
    // 执行_handleBuildScheduled回调
    onBuildScheduled();
  }
  // 添加到 dirty 元素列表
  _dirtyElements.add(element);
  element._inDirtyList = true;
}

在《框架启动源码剖析》一文中,我们剖析过WidgetsBinding的初始化

/// [WidgetsBinding]

void initInstances() {
  super.initInstances();
  _instance = this;

  _buildOwner = BuildOwner();
  buildOwner.onBuildScheduled = _handleBuildScheduled;
  /// ......省略......
}


void _handleBuildScheduled() {
    // 如果我们正在构建dirty元素,则更改不应触发新的帧
    ensureVisualUpdate();
}

// 如果此对象当前没有产生一个帧,则使用 scheduleFrame 调度一个新的帧
void ensureVisualUpdate() {
    switch (schedulerPhase) {
        case SchedulerPhase.idle:
        case SchedulerPhase.postFrameCallbacks:
            scheduleFrame();
            return;
        case SchedulerPhase.transientCallbacks:
        case SchedulerPhase.midFrameMicrotasks:
        case SchedulerPhase.persistentCallbacks:
            return;
    }
}

schedulerPhase的初始值是SchedulerPhase.idle,上面的分支执行scheduleFrame

/// [SchedulerBinding]

void scheduleFrame() {
  // 当应用在前台可见时,_framesEnabled的值为true
  // 这里如果App切到后台,直接返回
  if (_hasScheduledFrame || !framesEnabled)
    return;

  ensureFrameCallbacksRegistered();
  // 执行引擎中的调度方法
  window.scheduleFrame();
  _hasScheduledFrame = true;
}

void ensureFrameCallbacksRegistered() {
    window.onBeginFrame ??= _handleBeginFrame;
    window.onDrawFrame ??= _handleDrawFrame;
}

这里的scheduleFrame方法是一个引擎中的native方法,由C++实现,类似Java的JNI方法。官方对该方法做了解释

请求在下一个适当的机会调用onBeginFrameonDrawFrame回调

void scheduleFrame() native 'Window_scheduleFrame';

这里就不跟踪C++中的具体实现了,依照给出的解释,我们查看onBeginFrameonDrawFrame回调

/// [SchedulerBinding]

void _handleBeginFrame(Duration rawTimeStamp) {
  if (_warmUpFrame) {
    _ignoreNextEngineDrawFrame = true;
    return;
  }
  handleBeginFrame(rawTimeStamp);
}

void _handleDrawFrame() {
  if (_ignoreNextEngineDrawFrame) {
    _ignoreNextEngineDrawFrame = false;
    return;
  }
  handleDrawFrame();
}

到这里,正好对应上了我们在《框架启动源码剖析》渲染部分剖析的逻辑,关于handleDrawFrame的后续调用,请 跳转前文,这里直接查看我们关注的逻辑,主要在buildOwnerbuildScope方法中

/// [WidgetsBinding]

void drawFrame() {
    // ......省略......
    try {
        if (renderViewElement != null)
          // 将被标记为dirty的Element进行rebuild()
          buildOwner.buildScope(renderViewElement);
        // 调用父类的drawFrame,这里实际上调用的是RendererBinding中的drawFrame()方法
        super.drawFrame();
        // 通过卸载任何不再活动的元素来完成元素构建过程
        buildOwner.finalizeTree();
     }
    // ......省略......
}
/// [BuildOwner]

void buildScope(Element context, [ VoidCallback callback ]) {
  if (callback == null && _dirtyElements.isEmpty)
    return;

  Timeline.startSync('Build', arguments: timelineWhitelistArguments);
  try {
    _scheduledFlushDirtyElements = true;
    if (callback != null) {
      Element debugPreviousBuildTarget;
      _dirtyElementsNeedsResorting = false;
      try {
        callback();
      } finally {

      }
    }
      // 将dirty元素列表进行了排序
    _dirtyElements.sort(Element._sort);
    _dirtyElementsNeedsResorting = false;
    int dirtyCount = _dirtyElements.length;
    int index = 0;
    while (index < dirtyCount) {
      try {
          // 调用元素的rebuild
        _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) {
          index -= 1;
        }
      }
    }
  } finally {
    for (final Element element in _dirtyElements) {
      element._inDirtyList = false;
    }
    _dirtyElements.clear();
    _scheduledFlushDirtyElements = false;
    _dirtyElementsNeedsResorting = null;
    Timeline.finishSync();
  }
}
/// [Element]

void rebuild() {

  if (!_active || !_dirty)
    return;
  // 执行重建
  performRebuild();
}

这里performRebuild()是在其子类ComponentElement中实现的

/// [ComponentElement]

void performRebuild() {
  if (!kReleaseMode && debugProfileBuildsEnabled)
    Timeline.startSync('${widget.runtimeType}',  arguments: timelineWhitelistArguments);

  Widget built;
  try {
    // 调用与该元素关联的Widget的build方法,重建控件树
    built = build();
    debugWidgetBuilderValue(widget, built);
  } catch (e, stack) {

  } finally {
    // 我们将元素标记为clean的时间延迟到调用build()之后,这样在build()期间尝试markNeedsBuild()就会被忽略
    _dirty = false;
  }
  try {
    // 更新元素树
    _child = updateChild(_child, built, slot);
  } catch (e, stack) {

  }

  if (!kReleaseMode && debugProfileBuildsEnabled)
    Timeline.finishSync();
}

这里元素树的更新逻辑,都在updateChild方法中,前面已经分析过,参见《框架启动源码剖析》

我们还可以看一下build()方法的具体实现,不同的Element,其实现不同

/// StatelessWidget对应的Element
class StatelessElement extends ComponentElement {

  StatelessElement(StatelessWidget widget) : super(widget);

  @override
  StatelessWidget get widget => super.widget as StatelessWidget;

  // build中的context参数实际上就是Widget对应的Element对象
  @override
  Widget build() => widget.build(this);

  @override
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    _dirty = true;
    rebuild();
  }
}
/// StatefulWidget对应的Element
class StatefulElement extends ComponentElement {

  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {


    _state._element = this;

    _state._widget = widget;
  }

  @override
  Widget build() => _state.build(this);
    
  State get state => _state;
  State _state;
  
 // ......省略......
}

渲染过程

当需要更新页面的时候,由应用上层通知到Engine,Engine会等到下个Vsync信号到达的时候,去通知Framework上层,然后Framework会进行Animation, BuildLayoutCompositingPaint,最后生成layer提交给Engine。Engine会把layer进行组合,生成纹理,最后通过OpenGl接口提交数据给GPU, GPU经过处理后在显示器上面显示

Flutter 页面更新流程剖析_第1张图片

Flutter 页面更新流程剖析_第2张图片

视频课程

如需要获取完整的Flutter全栈式开发课程,请访问以下地址
Flutter全栈式开发之Dart 编程指南
二维码

Flutter 全栈式开发指南

你可能感兴趣的:(Dart与Flutter开发,Flutter源码剖析,dart,Flutter全栈式,移动开发,跨平台)