Flutter中setState原理及更新机制

5,073 阅读8分钟

前言

有状态组件StatefulWidget中, 更新状态需要调用setState({})方法。比如, 下面程序, 更新_color变量, 需要在setState({})里面。

class TestBuildApp extends StatefulWidget {
  const TestBuildApp({Key? key}) : super(key: key);

  @override
  _TestBuildAppState createState() => _TestBuildAppState();
}

class _TestBuildAppState extends State<TestBuildApp> {
  Color _color = Colors.redAccent;

  String _title = '6666666666666';

  StreamController _streamController = StreamController();

  void _incrementCounter() {
    setState(() {
      _color = Colors.blue;
      _title='99999999999999999';
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          // This is the theme of your application.
          primarySwatch: Colors.blue,
        ),
        home: Scaffold(
          body: _BuildPage(color: _color, title: _title,),
          // body: BuildPage(color: _color, title: _title,),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
        ),
    );
  }
}

那么, setState({})是怎样更新状态的呢?setState({})更新状态的原理和执行流程是怎样的呢?带着问题, 我们一起去源码里面学习。

setState

abstract class State<T extends StatefulWidget> with Diagnosticable {
  
  StatefulElement? _element;

  /// Whether this [State] object is currently in a tree.
  ///
  /// After creating a [State] object and before calling [initState], the
  /// framework "mounts" the [State] object by associating it with a
  /// [BuildContext]. The [State] object remains mounted until the framework
  /// calls [dispose], after which time the framework will never ask the [State]
  /// object to [build] again.
  ///
  /// It is an error to call [setState] unless [mounted] is true.
  bool get mounted => _element != null;

  @protected
  void setState(VoidCallback fn) {
    assert(fn != null);
    final Object? result = fn() as dynamic;
  
    /// 当前 _element执行markNeedsBuild(), 去
    _element!.markNeedsBuild();
  }

  
  @protected
  Widget build(BuildContext context);
}

setState({})方法中, 很清晰地知道, _element调用markNeedsBuild()方法, 标记当前_element需要更新。我们去_element里面看看。

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.
///
/// Since it is inefficient to build an element twice in one frame,
/// applications and widgets should be structured so as to only mark
/// widgets dirty during event handlers before the frame begins, not during
/// the build itself.
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); /// 调用scheduleBuildFor方法, 传入当前Element对象
 }
}

markNeedsBuild方法执行了几步关键操作

  • 标记当前Element对象为_dirty脏元素
  • BuildOwner对象拿到当前Element对象, 调用scheduleBuildFor()方法

接下来, 我们一起看看BuildOwner源码。

BuildOwner

class BuildOwner {
    
    BuildOwner({ this.onBuildScheduled, FocusManager? focusManager }) :
        focusManager = focusManager ?? (FocusManager()..registerGlobalHandlers());

    /// Called on each build pass when the first buildable element is marked
    /// dirty.
    VoidCallback? onBuildScheduled;

    /// Adds an element to the dirty elements list so that it will be rebuilt
    /// when [WidgetsBinding.drawFrame] calls [buildScope].
    void scheduleBuildFor(Element element) {
      assert(element != null);
      assert(element.owner == this);
      if (element._inDirtyList) {
        _dirtyElementsNeedsResorting = true;
        return;
      }
      if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
        _scheduledFlushDirtyElements = true;
        onBuildScheduled!(); /// 回调
      }
      _dirtyElements.add(element); /// 把传进来的element加入脏元素链表
      element._inDirtyList = true;
    }
}

scheduleBuildFor()注释: 把一个 element 添加到 _dirtyElements 链表,以便当WidgetsBinding.drawFrame中调用 buildScope 的时候能够重构 element。

我们可以很清晰地知道

  • 会去执行onBuildScheduled!()回调
  • 将标记为_dirty脏元素的Element对象, 加入_dirtyElements列表

onBuildScheduled!()是干嘛的呢? 从源码可知, onBuildScheduled是在BuildOwner类初始化的时候, 传进来的。

BuildOwnerWidgetsBindinginitInstances里初始化。

那么, 我们去到WidgetsBinding里面看看。

WidgetsBinding

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回调
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    assert(() {
      FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
      return true;
    }());
  }
  
    void _handleBuildScheduled() {
      // If we're in the process of building dirty elements, then changes
      // should not trigger a new frame.
      
      /// 执行ensureVisualUpdate
      ensureVisualUpdate();
    }

}
  • BuildOwner对象执行onBuildScheduled!()回调时, 会去执行WidgetsBinding类中的_handleBuildScheduled()方法
  • _handleBuildScheduled会去执行ensureVisualUpdate方法

那么, ensureVisualUpdate是干嘛的呢?ensureVisualUpdateSchedulerBinding类中

SchedulerBinding

mixin SchedulerBinding on BindingBase {
    
    /// Schedules a new frame using [scheduleFrame] if this object is not
    /// currently producing a frame.
    ///
    /// Calling this method ensures that [handleDrawFrame] will eventually be
    /// called, unless it's already in progress.
    ///
    /// 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(); /// 请求新的frame
          return;
        case SchedulerPhase.transientCallbacks:
        case SchedulerPhase.midFrameMicrotasks:
        case SchedulerPhase.persistentCallbacks:
          return;
      }
    }
    
    
    /// If necessary, schedules a new frame by calling
    /// [dart:ui.PlatformDispatcher.scheduleFrame].
    ///
    /// After this is called, the engine will (eventually) call
    /// [handleBeginFrame]. (This call might be delayed, e.g. if the device's
    /// screen is turned off it will typically be delayed until the screen is on
    /// and the application is visible.) Calling this during a frame forces
    /// another frame to be scheduled, even if the current frame has not yet
    /// completed.
    ///
    /// Scheduled frames are serviced when triggered by a "Vsync" signal provided
    /// by the operating system. The "Vsync" signal, or vertical synchronization
    /// signal, was historically related to the display refresh, at a time when
    /// hardware physically moved a beam of electrons vertically between updates
    /// of the display. The operation of contemporary hardware is somewhat more
    /// subtle and complicated, but the conceptual "Vsync" refresh signal continue
    /// to be used to indicate when applications should update their rendering.
    ///
    /// To have a stack trace printed to the console any time this function
    /// schedules a frame, set [debugPrintScheduleFrameStacks] to true.
    ///
    /// See also:
    ///
    ///  * [scheduleForcedFrame], which ignores the [lifecycleState] when
    ///    scheduling a frame.
    ///  * [scheduleWarmUpFrame], which ignores the "Vsync" signal entirely and
    ///    triggers a frame immediately.
    ///  请求新的frame
    void scheduleFrame() {
      if (_hasScheduledFrame || !framesEnabled)
        return;
      assert(() {
        if (debugPrintScheduleFrameStacks)
          debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
        return true;
      }());
      
      /// 确保帧渲染的回调已经被注册
      ensureFrameCallbacksRegistered();
      /// window调度帧
      window.scheduleFrame();
      _hasScheduledFrame = true;
    }

    /// Ensures callbacks for [PlatformDispatcher.onBeginFrame] and
    /// [PlatformDispatcher.onDrawFrame] are registered.
    @protected
    void ensureFrameCallbacksRegistered() {
      window.onBeginFrame ??= _handleBeginFrame;
      window.onDrawFrame ??= _handleDrawFrame;
    }

}

ensureVisualUpdate方法中, 根据不同的schedulerPhase枚举值, 做对应的处理。

schedulerPhase为SchedulerPhase.idleSchedulerPhase.postFrameCallbacks时调用scheduleFrame()方法

schedulerPhase枚举值解释如下

SchedulerPhase

Flutter 应用执行过程简单来讲分为 idle 和 frame 两种状态,idle 状态代表没有 frame 处理,如果应用状态改变需要刷新 UI,则需要通过scheduleFrame()去请求新的 frame,当 frame 到来时,就进入了frame状态,整个Flutter应用生命周期就是在 idle 和 frame 两种状态间切换。

当有新的 frame 到来时,开始调用 SchedulerBinding.handleDrawFrame 来处理 frame,具体处理过程就是依次执行四个任务队列:transientCallbacks、midFrameMicrotasks、persistentCallbacks、postFrameCallbacks,当四个任务队列执行完毕后当前 frame 结束。

综上,Flutter 将整个生命周期分为五种状态,通过 SchedulerPhase 枚举类来表示它们:

enum SchedulerPhase {
  
  /// 空闲状态,并没有 frame 在处理。这种状态代表页面未发生变化,并不需要重新渲染。
  /// 如果页面发生变化,需要调用`scheduleFrame()`来请求 frame。
  /// 注意,空闲状态只是指没有 frame 在处理,通常微任务、定时器回调或者用户事件回调都
  /// 可能被执行,比如监听了tap事件,用户点击后我们 onTap 回调就是在idle阶段被执行的。
  idle,

  /// 执行”临时“回调任务,”临时“回调任务只能被执行一次,执行后会被移出”临时“任务队列。
  /// 典型的代表就是动画回调会在该阶段执行。
  transientCallbacks,

  /// 在执行临时任务时可能会产生一些新的微任务,比如在执行第一个临时任务时创建了一个
  /// Future,且这个 Future 在所有临时任务执行完毕前就已经 resolve 了,这中情况
  /// Future 的回调将在[midFrameMicrotasks]阶段执行
  midFrameMicrotasks,

  /// 执行一些持久的任务(每一个frame都要执行的任务),比如渲染管线(构建、布局、绘制)
  /// 就是在该任务队列中执行的.
  persistentCallbacks,

  /// 在当前 frame 在结束之前将会执行 postFrameCallbacks,通常进行一些清理工作和
  /// 请求新的 frame。
  postFrameCallbacks,
}

下面, 我们再回到scheduleFrame()看看。

scheduleFrame()

mixin SchedulerBinding on BindingBase {
    
    /// If necessary, schedules a new frame by calling
    /// [dart:ui.PlatformDispatcher.scheduleFrame].
    ///
    /// After this is called, the engine will (eventually) call
    /// [handleBeginFrame]. (This call might be delayed, e.g. if the device's
    /// screen is turned off it will typically be delayed until the screen is on
    /// and the application is visible.) Calling this during a frame forces
    /// another frame to be scheduled, even if the current frame has not yet
    /// completed.
    ///
    /// Scheduled frames are serviced when triggered by a "Vsync" signal provided
    /// by the operating system. The "Vsync" signal, or vertical synchronization
    /// signal, was historically related to the display refresh, at a time when
    /// hardware physically moved a beam of electrons vertically between updates
    /// of the display. The operation of contemporary hardware is somewhat more
    /// subtle and complicated, but the conceptual "Vsync" refresh signal continue
    /// to be used to indicate when applications should update their rendering.
    ///
    /// To have a stack trace printed to the console any time this function
    /// schedules a frame, set [debugPrintScheduleFrameStacks] to true.
    ///
    /// See also:
    ///
    ///  * [scheduleForcedFrame], which ignores the [lifecycleState] when
    ///    scheduling a frame.
    ///  * [scheduleWarmUpFrame], which ignores the "Vsync" signal entirely and
    ///    triggers a frame immediately.
    ///  请求新的frame
    void scheduleFrame() {
      if (_hasScheduledFrame || !framesEnabled)
        return;
      
      /// 确保帧渲染的回调已经被注册
      ensureFrameCallbacksRegistered();
      
      /// window调度帧
      window.scheduleFrame();
      _hasScheduledFrame = true;
    }

    /// Ensures callbacks for [PlatformDispatcher.onBeginFrame] and
    /// [PlatformDispatcher.onDrawFrame] are registered.
    @protected
    void ensureFrameCallbacksRegistered() {
      window.onBeginFrame ??= _handleBeginFrame;
      window.onDrawFrame ??= _handleDrawFrame;
    }
}

ensureFrameCallbacksRegistered

/// Ensures callbacks for [PlatformDispatcher.onBeginFrame] and
    /// [PlatformDispatcher.onDrawFrame] are registered.
    @protected
    void ensureFrameCallbacksRegistered() {
      window.onBeginFrame ??= _handleBeginFrame;
      window.onDrawFrame ??= _handleDrawFrame;
    }

ensureFrameCallbacksRegistered方法中, 配置了_handleBeginFrame_handleDrawFrame两个回调。

_handleBeginFrame

void _handleBeginFrame(Duration rawTimeStamp) {
  if (_warmUpFrame) {
    // "begin frame" and "draw frame" must strictly alternate. Therefore
    // _rescheduleAfterWarmUpFrame cannot possibly be true here as it is
    // reset by _handleDrawFrame.
    assert(!_rescheduleAfterWarmUpFrame);
    _rescheduleAfterWarmUpFrame = true;
    return;
  }
  handleBeginFrame(rawTimeStamp);
}

_handleBeginFrame会去执行handleBeginFrame

handleBeginFrame

void handleBeginFrame(Duration? rawTimeStamp) {
  _frameTimelineTask?.start('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
  _firstRawTimeStampInEpoch ??= rawTimeStamp;
  _currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
  if (rawTimeStamp != null)
    _lastRawTimeStamp = rawTimeStamp;

  assert(() {
    _debugFrameNumber += 1;

    if (debugPrintBeginFrameBanner || debugPrintEndFrameBanner) {
      final StringBuffer frameTimeStampDescription = StringBuffer();
      if (rawTimeStamp != null) {
        _debugDescribeTimeStamp(_currentFrameTimeStamp!, frameTimeStampDescription);
      } else {
        frameTimeStampDescription.write('(warm-up frame)');
      }
      _debugBanner = '▄▄▄▄▄▄▄▄ Frame ${_debugFrameNumber.toString().padRight(7)}   ${frameTimeStampDescription.toString().padLeft(18)} ▄▄▄▄▄▄▄▄';
      if (debugPrintBeginFrameBanner)
        debugPrint(_debugBanner);
    }
    return true;
  }());

  assert(schedulerPhase == SchedulerPhase.idle);
  
  /// 此时阶段等于SchedulerPhase.idle;
  _hasScheduledFrame = false;
  
  try {
  
    // TRANSIENT FRAME CALLBACKS
    _frameTimelineTask?.start('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
    
    /// 执行动画的回调方法 schedulerPhase.transientCallbacks;
    _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.midFrameMicrotasks;
    _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
  }
}

主要就是遍历_transientCallbacks,执行相应的Animate操作, 可通过scheduleFrameCallback()/cancelFrameCallbackWithId()来完成添加和删除成员

最后将调度状态更新到SchedulerPhase.midFrameMicrotasks

_handleDrawFrame

void _handleDrawFrame() {
  if (_rescheduleAfterWarmUpFrame) {
    _rescheduleAfterWarmUpFrame = false;
    // Reschedule in a post-frame callback to allow the draw-frame phase of
    // the warm-up frame to finish.
    addPostFrameCallback((Duration timeStamp) {
      // Force an engine frame.
      //
      // We need to reset _hasScheduledFrame here because we cancelled the
      // original engine frame, and therefore did not run handleBeginFrame
      // who is responsible for resetting it. So if a frame callback set this
      // to true in the "begin frame" part of the warm-up frame, it will
      // still be true here and cause us to skip scheduling an engine frame.
      _hasScheduledFrame = false;
      scheduleFrame();
    });
    return;
  }
  handleDrawFrame();
}

_handleDrawFrame会去执行handleDrawFrame方法。 根据注释, [handleBeginFrame]执行后, 会立即执行handleDrawFrame方法。

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].
///
/// See [handleBeginFrame] for a discussion about debugging hooks that may be
/// useful when working with frame callbacks.
void handleDrawFrame() {
/// 确保当前状态是 SchedulerPhase.midFrameMicrotasks
  assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
  /// 动画结束
  _frameTimelineTask?.finish(); // end the "Animate" phase
  try {
    // PERSISTENT FRAME CALLBACKS
    /// 执行PERSISTENT FRAME回调, 主要包括build\layout\draw流程
    ///  SchedulerPhase.persistentCallbacks;
    _schedulerPhase = SchedulerPhase.persistentCallbacks;
    for (final FrameCallback callback in _persistentCallbacks)
      _invokeFrameCallback(callback, _currentFrameTimeStamp!);

    /// SchedulerPhase.postFrameCallbacks 
    /// 执行POST-FRAME回调, 主要是状态清理,准备调度下一帧frame绘制请求
    // 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.idle 空闲状态
    _schedulerPhase = SchedulerPhase.idle;
    /// 标识结束”Frame“阶段
    _frameTimelineTask?.finish(); // end the Frame
    assert(() {
      if (debugPrintEndFrameBanner)
        debugPrint('▀' * _debugBanner!.length);
      _debugBanner = null;
      return true;
    }());
    _currentFrameTimeStamp = null;
  }
} 

/ Calls the given [callback] with [timestamp] as argument.
//
// Wraps the callback in a try/catch and forwards any error to
// [debugSchedulerExceptionHandler], if set. If not set, then simply prints
// the error.
@pragma('vm:notify-debugger-on-exception')
void _invokeFrameCallback(FrameCallback callback, Duration timeStamp, [ StackTrace? callbackStack ]) {
  assert(callback != null);
  assert(_FrameCallbackEntry.debugCurrentCallbackStack == null);
  assert(() {
    _FrameCallbackEntry.debugCurrentCallbackStack = callbackStack;
    return true;
  }());
  try {
    callback(timeStamp);
  } catch (exception, exceptionStack) {
    FlutterError.reportError(FlutterErrorDetails(
      exception: exception,
      stack: exceptionStack,
      library: 'scheduler library',
      context: ErrorDescription('during a scheduler callback'),
      informationCollector: (callbackStack == null) ? null : () sync* {
        yield DiagnosticsStackTrace(
          '\nThis exception was thrown in the context of a scheduler callback. '
          'When the scheduler callback was _registered_ (as opposed to when the '
          'exception was thrown), this was the stack',
          callbackStack,
        );
      },
    ));
  }
  assert(() {
    _FrameCallbackEntry.debugCurrentCallbackStack = null;
    return true;
  }());
}

该方法主要功能:

  • 遍历_persistentCallbacks,执行相应的回调方法,可通过addPersistentFrameCallback()注册,一旦注册后不可移除,后续每一次frame回调都会执行
  • _persistentCallbacks主要包括build\layout\draw流程
  • 遍历_postFrameCallbacks,执行相应的回调方法,可通过addPostFrameCallback()注册,handleDrawFrame()执行完成后会清空_postFrameCallbacks内容
  • _postFrameCallbacks主要是状态清理,准备调度下一帧frame绘制请求

探究源码到这里, 我们心里多了疑惑。handleDrawFrame是去绘制帧, 绘制帧应该在RenderObject类里呀。handleDrawFrame只是循环执行了_persistentCallbacks_postFrameCallbacks里面的方法。那么, _persistentCallbacks_postFrameCallbacks数组里的值, 从哪里添加的呢?SchedulerBinding类又是怎样跟绘制绑定类RendererBinding关联的呢?

答案是有的。

runApp启动整个flutter程序, 加载根节点的时候, RendererBinding初始化的时候, 已经有了交代。

RendererBinding

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    
    /// 初始化的时候, 执行addPersistentFrameCallback
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
    if (kIsWeb) {
      addPostFrameCallback(_handleWebFirstFrame);
    }
  }
  
  void _handlePersistentFrameCallback(Duration timeStamp) {
     /// 执行绘制帧
     drawFrame();
     _scheduleMouseTrackerUpdate();
   }
}

RendererBinding类初始化的时候, 加载一个持久化绘制帧任务_handlePersistentFrameCallback回调, 并存储到SchedulerBinding类的_persistentCallbacks数组里面。等到合适的时机, 去_persistentCallbacks数组里面取出去, 并执行。合适的时机有哪些呢?

  • runApp(Widget app)中的scheduleWarmUpFrame()
  • setSate({})

我们可以在demo, debug模式, 跟踪具体流程。

以上配置好ensureFrameCallbacksRegistered()回调后。真正激活ensureFrameCallbacksRegistered()回调并响应绘制流程, 还需要关键一步, 那就是调用window.scheduleFrame()。这样, 就激活程序, 去RendererBinding绑定类绘制了。

我们一起学习window.scheduleFrame()

window.scheduleFrame()

void scheduleFrame() => platformDispatcher.scheduleFrame();

platformDispatcher执行scheduleFrame()

void scheduleFrame() native 'PlatformConfiguration_scheduleFrame';

上面程序, 最终会执行到引擎层面PlatformConfiguration_scheduleFrame

c++ 引擎

void ScheduleFrame(Dart_NativeArguments args) {
// 看下方 RuntimeController::ScheduleFrame
  UIDartState::Current()->window()->client()->ScheduleFrame();
}

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

Engine::ScheduleFrame

void RuntimeController::ScheduleFrame() {
  client_.ScheduleFrame(); // 看下面Engine::ScheduleFrame
}

这里推荐查看袁辉辉大佬的:Flutter渲染机制—UI线程 文中小节[2.1]介绍Engine::ScheduleFrame()经过层层调用,最终会注册Vsync回调。 等待下一次vsync信号的到来, 然后再经过层层调用最终会调用到Window::BeginFrame()

Window::BeginFrame

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绘制过程。

可以清晰地知道, window.scheduleFrame()执行后, window.onBeginFramewindow.onDrawFrame将紧接着会在合适时机被调用。

总结

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

参考资料

Flutter的setState更新原理和流程

Flutter启动流程和渲染管线

Flutter渲染机制—UI线程

深入理解setState更新机制

Flutter之SchedulerBinding简析

Flutter 学习之旅(三十九) Flutter RenderObjcet(一)layout布局过程

Flutter 学习之旅(四十) Flutter RenderObjcet(二)layout 布局计算过程

原来我一直在错误的使用 setState()?

Flutter入门与实战:从源码分析setState 的时候到底发生了什么?