美文网首页
Flutter - setState更新机制

Flutter - setState更新机制

作者: wethereornot | 来源:发表于2020-11-30 18:26 被阅读0次

    基于Flutter 1.22.3的源码刨析,分析Flutter的StatefulWidget的UI更新机制,相关源码:

      widgets/framework.dart
      widgets/binding.dart
      scheduler/binding.dart
      lib/ui/window.dart
      flutter/runtime/runtime_controller.cc
    

    一、概述

    对于Flutter来说,万物皆为Widget,常见的Widget子类为StatelessWidget(无状态)和StatefulWidget(有状态);

    • StatelessWidget:内部没有保存状态,界面创建后不会发生改变;
    • StatefulWidget:内部有保存状态,当状态发生改变,调用setState()方法会触发StatefulWidget的UI发生更新,对于自定义继承自StatefulWidget的子类,必须要重写createState()方法。

    接下来看看setState()究竟干了哪些操作。

    二、Widget更新流程

    2.1 setState

    [-> framework.dart::state]

    abstract class State<T extends StatefulWidget> with Diagnosticable {
    StatefulElement _element;
      /// It is an error to call this method after the framework calls [dispose].
      /// You can determine whether it is legal to call this method by checking
      /// whether the [mounted] property is true.
      @protected
      void setState(VoidCallback fn) {
       ...
        _element.markNeedsBuild();
      }
    }
    

    这里需要注意setState()方法要在dispose()方法调用前调用,可以通过mounted属性值来判断父Widget是否还包含该Widget.

    2.2 markNeedsBuild

    [->framework.dart::Element]

    abstract class Element extends DiagnosticableTree implements BuildContext {
      /// Marks the element as dirty and adds it to the global list of widgets to
      /// rebuild in the next frame.
      ///标记元素为脏元素,然后添加到list集合里 等下一帧刷新
        void markNeedsBuild() {
          if (!_active)
            return;
          if (dirty)
            return;
          _dirty = true;
          owner.scheduleBuildFor(this);
        }
    }
    

    设置 Element的 _dirty 为 true

    2.3 scheduleBuildFor

    [->framework.dart::BuildOwner]

    abstract class Element extends DiagnosticableTree implements BuildContext {
      /// Adds an element to the dirty elements list so that it will be rebuilt
      /// when [WidgetsBinding.drawFrame] calls [buildScope].
     void scheduleBuildFor(Element element) {  
      ...
       if (element._inDirtyList) { //是否在集合里面
            _dirtyElementsNeedsResorting = true;
            return;
          }
          if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
            _scheduledFlushDirtyElements = true;
            onBuildScheduled();
          }
          _dirtyElements.add(element);//记录所有脏元素
          element._inDirtyList = true;
      }
    }
    

    将element添加到脏element集合,之后会被重建

    2.4 _handleBuildScheduled

    [-> binding.dart:: WidgetsBinding]

    
    /// The glue between the widgets layer and the Flutter engine.
    mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
      @override
      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();
        buildOwner.onBuildScheduled = _handleBuildScheduled;
        window.onLocaleChanged = handleLocaleChanged;
        window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
        SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
        FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
      }
    
      void _handleBuildScheduled() {
        ensureVisualUpdate();
     }
    

    在Flutter应用启动过程初始化WidgetsBinding时,赋值onBuildScheduled等于_handleBuildScheduled()。

    2.5 ensureVisualUpdate

    [ -> binding.dart::SchedulerBinding]

    mixin SchedulerBinding on BindingBase {
      @override
      void initInstances() {
        super.initInstances();
        _instance = this;
      }
    
      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。SchedulerPhase是一个enum枚举类型,有以下5个可取值:

    状态 含义
    idle 没有正在处理的帧,可能正在执行的是WidgetsBinding.scheduleTask,scheduleMicrotask,Timer,事件handlers,或者其他回调等
    transientCallbacks SchedulerBinding.handleBeginFrame过程, 处理动画状态更新
    midFrameMicrotasks 处理transientCallbacks阶段触发的微任务(Microtasks)
    persistentCallbacks WidgetsBinding.drawFrame和SchedulerBinding.handleDrawFrame过程,build/layout/paint流水线工作
    postFrameCallbacks 主要是清理和计划执行下一帧的工作

    2.6 scheduleFrame

       void scheduleFrame() {
        //只有当APP处于用户可见状态才会准备调度下一帧方法
        if (_hasScheduledFrame || !framesEnabled)
          return;
        ensureFrameCallbacksRegistered();
        window.scheduleFrame();
        _hasScheduledFrame = true;
      }
    

    2.7 scheduleFrame

    [ -> lib/ui/window.dart::Window]

      void scheduleFrame() native 'PlatformConfiguration_scheduleFrame';
    

    window是Flutter引擎中跟图形相关接口打交道的核心类,这里是一个native方法

    2.7.1 ScheduleFrame(C++)

    [->引擎库 lib/ui/window/platform_configuration.cc]

      void ScheduleFrame(Dart_NativeArguments args) {
      UIDartState::ThrowIfUIOperationsProhibited();
      UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();
    }
    

    通过RegisterNatives()完成native方法的注册,“PlatformConfiguration_scheduleFrame”所对应的native方法如上所示。

    2.7.2 RuntimeController::ScheduleFrame

    [->runtime/runtime_controller.cc]

      // |PlatformConfigurationClient|
    void RuntimeController::ScheduleFrame() {
      client_.ScheduleFrame();
    }
    

    2.7.3 Engine::ScheduleFrame

    [->flutter/shell/common/engine.cc]

      void Engine::ScheduleFrame(bool regenerate_layer_tree) {
      animator_->RequestFrame(regenerate_layer_tree);
    }
    

    Engine::ScheduleFrame()经过层层调用,最终会注册Vsync回调。 等待下一次vsync信号的到来,然后再经过层层调用最终会调用到Window::BeginFrame()。这里不展开解释了。

    2.8 Window::BeginFrame

    [-> flutter/lib/ui/window/window.cc]

      void Window::BeginFrame(fml::TimePoint frameTime) {
      std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
      if (!dart_state)
        return;
      tonic::DartState::Scope scope(dart_state);
    
      int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds();
    
      DartInvokeField(library_.value(), "_beginFrame",
                      {
                          Dart_NewInteger(microseconds),
                      });
    
      //执行MicroTask
      UIDartState::Current()->FlushMicrotasksNow();
    
      DartInvokeField(library_.value(), "_drawFrame", {});
    }
    

    Window::BeginFrame()过程主要工作:

    • 执行_beginFrame
    • 执行FlushMicrotasksNow
    • 执行_drawFrame

    可见,Microtask位于beginFrame和drawFrame之间,那么Microtask的耗时会影响ui绘制过程。

    2.9handleBeginFrame

    [-> lib/src/scheduler/binding.dart:: SchedulerBinding]

      void handleBeginFrame(Duration rawTimeStamp) {
      Timeline.startSync('Frame', arguments: timelineWhitelistArguments);
      _firstRawTimeStampInEpoch ??= rawTimeStamp;
      _currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
      if (rawTimeStamp != null)
        _lastRawTimeStamp = rawTimeStamp;
    
      profile(() {
        _profileFrameNumber += 1;
        _profileFrameStopwatch.reset();
        _profileFrameStopwatch.start();
      });
    
      //此时阶段等于SchedulerPhase.idle;
      _hasScheduledFrame = false;
      try {
        Timeline.startSync('Animate', arguments: timelineWhitelistArguments);
        _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;
      }
    }
    

    该方法主要功能是遍历_transientCallbacks,执行相应的Animate操作,可通过scheduleFrameCallback()/cancelFrameCallbackWithId()来完成添加和删除成员,再来简单看看这两个方法。

    2.10 handleDrawFrame

    [-> lib/src/scheduler/binding.dart:: SchedulerBinding]

      void handleDrawFrame() {
      assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
      Timeline.finishSync(); // 标识结束"Animate"阶段
      try {
        _schedulerPhase = SchedulerPhase.persistentCallbacks;
        //执行PERSISTENT FRAME回调
        for (FrameCallback callback in _persistentCallbacks)
          _invokeFrameCallback(callback, _currentFrameTimeStamp);
    
        _schedulerPhase = SchedulerPhase.postFrameCallbacks;
        // 执行POST-FRAME回调
        final List<FrameCallback> localPostFrameCallbacks = List<FrameCallback>.from(_postFrameCallbacks);
        _postFrameCallbacks.clear();
        for (FrameCallback callback in localPostFrameCallbacks)
          _invokeFrameCallback(callback, _currentFrameTimeStamp);
      } finally {
        _schedulerPhase = SchedulerPhase.idle;
        Timeline.finishSync(); //标识结束”Frame“阶段
        profile(() {
          _profileFrameStopwatch.stop();
          _profileFramePostEvent();
        });
        _currentFrameTimeStamp = null;
      }
    }
    

    该方法主要功能:

    • 遍历_persistentCallbacks,执行相应的回调方法,可通过addPersistentFrameCallback()注册,一旦注册后不可移除,后续每一次frame回调都会执行;
    • 遍历_postFrameCallbacks,执行相应的回调方法,可通过addPostFrameCallback()注册,handleDrawFrame()执行完成后会清空_postFrameCallbacks内容。

    三、小结

    可见,setState()过程主要工作是记录所有的脏元素,添加到BuildOwner对象的_dirtyElements成员变量,然后调用scheduleFrame来注册Vsync回调。 当下一次vsync信号的到来时会执行handleBeginFrame()和handleDrawFrame()来更新UI。

    本文参考了gityan大佬的博客,感谢大佬分享!

    相关文章

      网友评论

          本文标题:Flutter - setState更新机制

          本文链接:https://www.haomeiwen.com/subject/efwniktx.html