Flutter中setState执行流程分析

469 阅读6分钟

setState流程源码分析 在Flutter中,当你调用setState方法后,Flutter框架会在下一个帧中进行重建并触发渲染流程。Flutter使用自己的渲染引擎进行绘制和渲染。

1.触发setState方法: State类中调用setState方法 StatefulElement类中执行markNeedsBuild方法。 @protected void setState(VoidCallback fn) { final dynamic result = fn() as dynamic; _element.markNeedsBuild(); }

●先执行我们的闭包函数fn(),也就说setState包裹的函数会先执行相关逻辑,然后执行markNeedsBuild

2.element节点标脏,在markNeedsBuild函数中将当前树进行标脏(标记为dirty),如果已经是脏树,那么直接返回。 markNeedsBuild 是Element中的方法。 owner:是BuildOwner ,BuildOwner是element的管理类,主要负责dirtyElement、inactiveElement、globalkey关联的element的管理。 void markNeedsBuild() { if (!_active) return; if (dirty) return; _dirty = true; owner.scheduleBuildFor(this); }

3.将element添加到脏列表,并且执行相关的回调。在BuildOwner类中执行scheduleBuildFor函数将一个元素标记为需要重新构建的状态,并将其添加到脏列表中。同时执行BuildOwner的回调函数。 final List _dirtyElements = []; ...... void scheduleBuildFor(Element element) { if (element._inDirtyList) { _dirtyElementsNeedsResorting = true; return; } if (!_scheduledFlushDirtyElements && onBuildScheduled != null) { _scheduledFlushDirtyElements = true; // 是一个回调方法 onBuildScheduled(); } _dirtyElements.add(element); element._inDirtyList = true; }

●BuildOwner对象执行onBuildScheduled!()回调时是怎么回事呢? ●其实是执行WidgetsBinding类中的_handleBuildScheduled()方法

4.BuildOwner对象拿到当前Element对象, 调用scheduleBuildFor()方法回调,执行 _handleBuildScheduled()方法, mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { @override void initInstances() { // 此方法会添加一个持久的帧回调。
super.initInstances(); _instance = this; //初始化_buildOwner对象 _buildOwner = BuildOwner(); // 配置onBuildScheduled回调 buildOwner!.onBuildScheduled = _handleBuildScheduled; window.onLocaleChanged = handleLocaleChanged; window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged; } void _handleBuildScheduled() { /// 执行ensureVisualUpdate ensureVisualUpdate(); } }

// 添加一个持久的帧回调。在应用程序的生命周期内,每一帧都被调用。 //在flutter app启动过程,也就是执行runApp过程会有WidgetsFlutterBinding初始化过程 mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { @override void initInstances() { super.initInstances(); ..... addPersistentFrameCallback(_handlePersistentFrameCallback); ...... }

// 添加每一帧的回调 (RendererBinding) final List _persistentCallbacks = []; void addPersistentFrameCallback(FrameCallback callback) { _persistentCallbacks.add(callback); }

// 执行回调时会执行handlePersistentFrameCallback方法(RendererBinding) void _handlePersistentFrameCallback(Duration timeStamp) { // 执行 RendererBinding的drawFrame方法 drawFrame(); _scheduleMouseTrackerUpdate(); }

●可以看到BuildOwner是在WidgetsBinding类中进行初始化的,在初始化时同时配置 onBuildScheduled回调函数,即BuildOwner对象执行onBuildScheduled!()回调时, 会去执行WidgetsBinding类中的_handleBuildScheduled()方法

5.SchedulerBinding类中执行ensureVisualUpdate方法。根据不同的调度任务执行不同的操作。 void ensureVisualUpdate() { switch (schedulerPhase) { // 没有正在处理的帧,可能正在执行的是 WidgetsBinding.scheduleTask, // scheduleMicrotask,Timer,事件 handlers,或者其他回调等 case SchedulerPhase.idle: // 主要是清理和计划执行下一帧的工作 case SchedulerPhase.postFrameCallbacks: scheduleFrame();//请求新的帧渲染。 return; // SchedulerBinding.handleBeginFrame 过程, 处理动画状态更新 case SchedulerPhase.transientCallbacks: // 处理 transientCallbacks 阶段触发的微任务(Microtasks) case SchedulerPhase.midFrameMicrotasks: // WidgetsBinding.drawFrame 和 SchedulerBinding.handleDrawFrame 过程, // build/layout/paint 流水线工作 case SchedulerPhase.persistentCallbacks: return; } }

6.在SchedulerBinding中 请求新的frame,注册 Vsync 信号 // 请求新的frame void scheduleFrame() { if (_hasScheduledFrame || !framesEnabled) return; //确保帧渲染的回调已经被注册 ensureFrameCallbacksRegistered(); // window调度帧 window.scheduleFrame(); _hasScheduledFrame = true; } @protected void ensureFrameCallbacksRegistered() { //处理渲染前的任务 window.onBeginFrame ??= _handleBeginFrame; //核心渲染流程 window.onDrawFrame ??= _handleDrawFrame; }

●Flutter 在 window 上注册一个 onBeginFrame和一个 onDrawFrame 回调,在onDrawFrame 回调中最终会调用 drawFrame。 ●当我们调用 window.scheduleFrame() 方法之后,Flutter引擎会在合适的时机(可以认为是在屏幕下一次刷新之前,具体取决于Flutter引擎的实现)来调用onBeginFrameonDrawFrame

7.当下一次刷新之前,会调用onBeginFrameonDrawFrame方法。 7.handleBeginFrame处理帧开始的操作。(SchedulerBinding) void _handleBeginFrame(Duration rawTimeStamp) { if (_warmUpFrame) { assert(!_rescheduleAfterWarmUpFrame); _rescheduleAfterWarmUpFrame = true; return; } handleBeginFrame(rawTimeStamp); }

●首先,检查_warmUpFrame标志。如果为true,表示当前是预热帧(warm-up frame)阶段。 ●在预热帧阶段,会执行以下操作: 断言(assert)_rescheduleAfterWarmUpFrame标志必须为false,这是为了确保在预热帧之后的绘制帧(draw frame)阶段,_rescheduleAfterWarmUpFrame已被重置。 将_rescheduleAfterWarmUpFrame标志设置为true,表示在预热帧之后需要重新调度帧渲染。返回,结束方法的执行。 如果不是预热帧阶段,则调用handleBeginFrame方法,将原始时间戳(rawTimeStamp)作为参数进行处理。 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

8.处理帧绘制 (SchedulerBinding) void handleDrawFrame() { Timeline.finishSync(); // end the "Animate" phase try { // 遍历_persistentCallbacks,执行相应的回调方法 //_persistentCallbacks主要包括build\layout\draw流程 _schedulerPhase = SchedulerPhase.persistentCallbacks; for (final FrameCallback callback in _persistentCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp); _schedulerPhase = SchedulerPhase.postFrameCallbacks; final List localPostFrameCallbacks = List.from(_postFrameCallbacks); _postFrameCallbacks.clear(); for (final FrameCallback callback in localPostFrameCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp); } finally { _schedulerPhase = SchedulerPhase.idle; Timeline.finishSync(); // end the Frame _currentFrameTimeStamp = null; } }

●_postFrameCallbacks可通过addPersistentFrameCallback()注册,一旦注册后不可移除,后续每一次frame回调都会执行,handleDrawFrame()执行完成后会清空_postFrameCallbacks内容,_postFrameCallbacks主要是状态清理,准备调度下一帧frame绘制请求。 ●当遍历_persistentCallbacks时会执行相应的回调方法也就是通过addPersistentFrameCallback添加注册的handlePersistentFrameCallback()方法。最后会执行RendererBinding的drawFrame方法

9.绘制渲染:RendererBinding的drawFrame方法,对对应的节点进行绘制渲染操作。 //但是在WidgetsBinding类中,也覆写了drawFrame方法。 WidgetsBinding继承了RendererBinding接口 @override void drawFrame() { TimingsCallback firstFrameCallback; if (_needToReportFirstFrame) { firstFrameCallback = (List timings) { if (!kReleaseMode) { developer.Timeline.instantSync('Rasterized first useful frame'); developer.postEvent('Flutter.FirstFrame', <String, dynamic>{}); } SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback); firstFrameCallback = null; _firstFrameCompleter.complete(); }; SchedulerBinding.instance.addTimingsCallback(firstFrameCallback); }

try {
  if (renderViewElement != null)
    buildOwner.buildScope(renderViewElement);
    //调用 RendererBinding的drawFrame方法
  super.drawFrame();
  buildOwner.finalizeTree();
} finally {
}
if (!kReleaseMode) {
  if (_needToReportFirstFrame && sendFramesToEngine) {
    developer.Timeline.instantSync('Widgets built first useful frame');
  }
}
_needToReportFirstFrame = false;
if (firstFrameCallback != null && !sendFramesToEngine) {
  SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
}

}

10.从dirtyElements列表中深到浅排序,之后遍历,遍历的过程中执行rebuild方法 void buildScope(Element context, [ VoidCallback callback ]) { if (callback == null && _dirtyElements.isEmpty) return; Timeline.startSync('Build', arguments: timelineWhitelistArguments); _dirtyElements.sort(Element._sort); _dirtyElementsNeedsResorting = false; int dirtyCount = _dirtyElements.length; int index = 0; while (index < dirtyCount) { try { _dirtyElements[index].rebuild(); } catch (e, stack) { }finally { for (final Element element in _dirtyElements) { element._inDirtyList = false; } _dirtyElements.clear(); _scheduledFlushDirtyElements = false; _dirtyElementsNeedsResorting = null; Timeline.finishSync(); } } } _dirtyElements列表,先从深到浅排序,之后遍历,遍历的过程中执行rebuild方法, 此时将所有标记为dirty的Element节点依次执行rebuild,preformRebuild,build,updateChild,update方法,执行界面更新。 所有绘制完成之后将_dirtyElements列表清空,_inDirtyList标记位置为false。

11.渲染引擎渲染 (RendererBinding) //super.drawFrame(),这里就执行到了RendererBinding类中,定义如下: void drawFrame() { pipelineOwner.flushLayout();//刷新布局 pipelineOwner.flushCompositingBits();//刷新合成位 pipelineOwner.flushPaint();//刷新绘制。 //是否需要将帧发送到GPU if (sendFramesToEngine) { renderView.compositeFrame(); // 将帧合成并发送到GPU pipelineOwner.flushSemantics(); //将语义信息发送到操作系统 _firstFrameSent = true;//表示已经发送了第一帧。 } }

●当你调用setState方法后,Flutter框架会在下一个帧中触发渲染流程(并不会立马执行build方法)通常每秒刷新60次(60帧每秒),对部件进行重建、布局、绘制和合成等操作,最终将渲染结果显示在屏幕上。可见,setState()过程主要工作是记录所有的脏元素,添加到BuildOwner对象的_dirtyElements成员变量,然后调用scheduleFrame来注册Vsync回调。 当下一次vsync信号的到来时会执行handleBeginFrame()和handleDrawFrame()来更新UI。 ●scheduleFrame 回调 C++ 注册 Vsync 信号 ●Vsnc 来临,回调 C++ 调用 ,

来看一个生命周期的图

rebuild调用:element第一次构建mount的时候 widget发生变化的时候 performRebuild调用:rebuild 中执行performRebuild(),这是个抽象方法,所以具体rebuild 的逻辑由element子类去实现。 build: 不同类型Element的实现不同:performRebuild中调用 RenderObjectElement ComponentElement updateChild: performRebuild中调用(更新子元素) deactivateChild:updateChild 中执行:如果newWidget是null,并且old child非null,直接 deactivateChild 。 update:updateChild 中执行 :child不为空 detachRenderObject:deactivateChild中执行