l

lizhao

V1

2022/08/28阅读:18主题:橙心

Flutter中setState更新原理探索

Flutter中setState更新机制探索

简述

有状态组件StatefulWidget更新状态调用setState(() {})函数实现数据更新

setState内部实现

精简代码

abstract class State<T extends StatefulWidget> with Diagnosticable {

 StatefulElement? _element;

 bool get mounted => _element != null;
 
 @protected
  void setState(VoidCallback fn) {
  
   final Object? result = fn() as dynamic;
  
   /// _element执行 markNeedsBuild
   _element!.markNeedsBuild();

}

通过上面代码可以看到setState(() {})函数中主要是_element执行markNeedsBuild()

markNeedsBuild


abstract class Element extends DiagnosticableTree implements BuildContext {


 void markNeedsBuild() {
 
   if (_lifecycleState != _ElementLifecycle.active)
       return;

   if (dirty)
      return;
    _dirty = true;
    owner!.scheduleBuildFor(this);

}

markNeedsBuild函数执行关键几步:

  1. 判断 _lifecycleState是否是_ElementLifecycle.active状态

  2. 当前元素是否标记为脏

  3. BuildOwner对象执行scheduleBuildFor函数,并传入当前元素Element

scheduleBuildFor

class BuildOwner {

 void scheduleBuildFor(Element element) {
 
 
 if (element._inDirtyList) {
     _dirtyElementsNeedsResorting = true;
     return;
  }
  
  if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
      _scheduledFlushDirtyElements = true;
      onBuildScheduled!();
    }
    _dirtyElements.add(element);
    element._inDirtyList = true;
  
    
}

}

  1. scheduleBuildFor把当前element加入_dirtyElements列表里面 并且标记当前元素的_inDirtyList为true
  2. 执行 onBuildScheduled回调
  3. onBuildScheduled 回调是在BuildOwner初始化的时候传进来的

onBuildScheduled()

onBuildScheduled()回调是在 WidgetsBinding中实现的


mixin WidgetsBinding on BindingBase, ... {
  
    _buildOwner = BuildOwner();
    
    buildOwner!.onBuildScheduled = _handleBuildScheduled;
 
  }


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

_handleBuildScheduled()

void _handleBuildScheduled() {
  ensureVisualUpdate();
}

_handleBuildScheduled函数执行ensureVisualUpdate函数

ensureVisualUpdate

ensureVisualUpdateSchedulerBinding类中

mixin SchedulerBinding on BindingBase {

void ensureVisualUpdate() {
    switch (schedulerPhase) {
      case SchedulerPhase.idle:
      case SchedulerPhase.postFrameCallbacks:
        scheduleFrame();
        return;
      case SchedulerPhase.transientCallbacks:
      case SchedulerPhase.midFrameMicrotasks:
      case SchedulerPhase.persistentCallbacks:
        return;
    }
  }

}


scheduleFrame()

void scheduleFrame() {
    if (_hasScheduledFrame || !framesEnabled)
      return;
   
    ensureFrameCallbacksRegistered();
    platformDispatcher.scheduleFrame();
    _hasScheduledFrame = true;
  }

scheduleFrame 函数里面调用了下面两个函数

  • ensureFrameCallbacksRegistered函数
  • platformDispatcher.scheduleFrame()函数;

platformDispatcher.scheduleFrame()

 void scheduleFrame() native 'PlatformConfiguration_scheduleFrame';

ensureFrameCallbacksRegistered();

@protected
  void ensureFrameCallbacksRegistered() {
    platformDispatcher.onBeginFrame ??= _handleBeginFrame;
    platformDispatcher.onDrawFrame ??= _handleDrawFrame;
  }
  

ensureFrameCallbacksRegistered 方法中给平台调度器设置了两个回调_handleBeginFrame_handleDrawFrame

_handleBeginFrame

  void _handleBeginFrame(Duration rawTimeStamp) {
    if (_warmUpFrame) {
      _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;

   
    _hasScheduledFrame = false;
    try {
      // TRANSIENT FRAME CALLBACKS
      _frameTimelineTask?.start('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
      _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;
    }
  }

handleBeginFrame 执行操作

  • _frameTimelineTask执行 frame动画
  • _frameTimelineTask执行 Animate动画
  • 遍历 _transientCallbacks回调
  • 最后将_schedulerPhase状态更新为SchedulerPhase.midFrameMicrotasks

_handleDrawFrame()

void _handleDrawFrame() {
    if (_rescheduleAfterWarmUpFrame) {
      _rescheduleAfterWarmUpFrame = false;
      addPostFrameCallback((Duration timeStamp) {
     
        _hasScheduledFrame = false;
        scheduleFrame();
      });
      return;
    }
    handleDrawFrame();
  }

_handleDrawFrame执行handleDrawFrame方法

handleDrawFrame()


 void handleDrawFrame() {
   assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
    _frameTimelineTask?.finish(); 
    
     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;
    }
 }
 
 

该方法主要执行操作

  • _frameTimelineTask结束动画操作

  • 遍历persistentCallbacks回调,执行 _invokeFrameCallback

  • 遍历_postFrameCallbacks 执行 _invokeFrameCallback,并且执行清空操作_postFrameCallbacks.clear();

  • 最后 _schedulerPhase、_frameTimelineTask相关状态还原,准备下一次更新

_persistentCallbacks 、_postFrameCallbacks 里面的方法在什么地方添加的

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,
    );
    platformDispatcher
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
    if (kIsWeb) {
      addPostFrameCallback(_handleWebFirstFrame);
    }
  }
  
  
  
  void _handlePersistentFrameCallback(Duration timeStamp) {
  /// 执行绘制
    drawFrame();
    _scheduleMouseTrackerUpdate();
  }

  • 可以看到 初始化的时候执行 addPersistentFrameCallback回调
  • 将这个回调存储到 SchedulerBinding类的_persistentCallbacks数组里面,等到用的时候取出来去执行

合适时机有哪些

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

总结

setState()过程主要工作是记录所有的脏元素,添加到BuildOwner对象的_dirtyElements成员变量,然后调用scheduleFrame来执行handleBeginFrame()和handleDrawFrame()更新UI

分类:

前端

标签:

前端

作者介绍

l
lizhao
V1