Flutter的setState更新原理和流程

本文來自整理和簡化

調用 setState()必須是沒有調用過 dispose()方法,不然出錯,可通過mounted屬性來判斷調用此方法是否合法。

if (mounted) {
  setState(() {});
}

清晰的看到在framework.dart內setstate方法除了一些條件判斷就是:

_element.markNeedsBuild();

那我們看看markNeedsBuild。

Element 類 markNeedsBuild方法

  void markNeedsBuild() {
    assert(_debugLifecycleState != _ElementLifecycle.defunct);
    if (!_active)
      return;//返回
     ...
    if (dirty)
      return;
    _dirty = true;
    //調用scheduleBuildFor方法
    owner.scheduleBuildFor(this);
  }

將 element 元素標記爲“髒”,並把它添加到全局的“髒”鏈表裏,以便在下一幀更新信號時更新.

  • 這裏的“ ”鏈表是待更新的鏈表,更新過後就不“髒”了。
  • 由於一幀做兩次更新有點低效,所以在_active=false 的時候直接返回。

那我們看看本方法最後調用的scheduleBuildFor方法。

BuildOwner 類 scheduleBuildFor方法

BuildOwner類是widget framework的管理類,它跟蹤那些需要重新構建的 widget。

void scheduleBuildFor(Element element) {
     ...
    if (element._inDirtyList) {
      ...
      _dirtyElementsNeedsResorting = true;
      return;
    }
    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
      _scheduledFlushDirtyElements = true;
      onBuildScheduled();//回調
    }
    _dirtyElements.add(element);//把element加入髒元素鏈表
    element._inDirtyList = true;
    assert(() {
      if (debugPrintScheduleBuildForStacks)
        debugPrint('...dirty list is now: $_dirtyElements');
      return true;
    }());
  }

把一個 element 添加到 _dirtyElements 鏈表,以便當WidgetsBinding.drawFrame中調用 buildScope 的時候能夠重構 element。onBuildScheduled()是一個 BuildOwner 的回調。

onBuildScheduled回調在WidgetsBinding的initInstances裏初始化。

mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    // 這裏
    buildOwner.onBuildScheduled = _handleBuildScheduled;
    window.onLocaleChanged = handleLocaleChanged;window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
 SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage);  FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
  }
}

我們可以看到buildOwner.onBuildScheduled回調等於了_handleBuildScheduled,那現在來看看這個_handleBuildScheduled方法:

void _handleBuildScheduled() {
    //調用ensureVisualUpdate
    ensureVisualUpdate();
  }

可以看到調用ensureVisualUpdate方法,那我們繼續走下去。

SchedulerBinding類ensureVisualUpdate方法

  void ensureVisualUpdate() {
    switch (schedulerPhase) {
      case SchedulerPhase.idle:
      case SchedulerPhase.postFrameCallbacks:
        //當schedulerPhase爲SchedulerPhase.idle,
        //SchedulerPhase.postFrameCallbacks時調用
        //scheduleFrame()
        scheduleFrame();
        return;
      case SchedulerPhase.transientCallbacks:
      case SchedulerPhase.midFrameMicrotasks:
      case SchedulerPhase.persistentCallbacks:
        return;
    }
  }

分別case了SchedulerPhase 的 5 個枚舉值:

狀態 含義
idle 沒有正在處理的幀,可能正在執行的是 WidgetsBinding.scheduleTask,scheduleMicrotask,Timer,事件 handlers,或者其他回調等
transientCallbacks SchedulerBinding.handleBeginFrame 過程, 處理動畫狀態更新
midFrameMicrotasks 處理 transientCallbacks 階段觸發的微任務(Microtasks)
persistentCallbacks WidgetsBinding.drawFrame 和 SchedulerBinding.handleDrawFrame 過程,build/layout/paint 流水線工作
postFrameCallbacks 主要是清理和計劃執行下一幀的工作

第二個case調用scheduleFrame()方法

那我們看看scheduleFrame()方法

  void scheduleFrame() {
  if (_hasScheduledFrame || !_framesEnabled) return;
  assert(() {
    if (debugPrintScheduleFrameStacks)
      debugPrintStack(
          label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
    return true;
  }());
  //調用Window 的scheduleFrame方法是一個 native 方法
  window.scheduleFrame();
  _hasScheduledFrame = true;
}

WidgetsFlutterBinding 混入的這些 Binding 中基本都是監聽並處理 Window 對象的一些事件,然後將這些事件按照 Framework 的模型包裝、抽象然後分發。可以看到 WidgetsFlutterBinding 正是粘連 Flutter engine 與上層 Framework 的“膠水”。

解釋
GestureBinding 提供了 window.onPointerDataPacket 回調,綁定 Framework 手勢子系統,是 Framework 事件模型與底層事件的綁定入口
ServicesBinding 提供了 window.onPlatformMessage 回調, 用於綁定平臺消息通道(message channel),主要處理原生和 Flutter 通信
SchedulerBinding 提供了 window.onBeginFrame 和 window.onDrawFrame 回調,監聽刷新事件,綁定 Framework 繪製調度子系統
PaintingBinding 綁定繪製庫,主要用於處理圖片緩存
SemanticsBinding 語義化層與 Flutter engine 的橋樑,主要是輔助功能的底層支持
RendererBinding 提供了 window.onMetricsChanged 、window.onTextScaleFactorChanged 等回調。它是渲染樹與 Flutter engine 的橋樑
WidgetsBinding 提供了 window.onLocaleChanged、onBuildScheduled 等回調。它是 Flutter widget 層與 engine 的橋樑

之前的文中有說過,UI 的繪製邏輯是在 Render 樹中實現的,所以這裏還來細看 RendererBinding 的邏輯。

RendererBinding

void initInstances() {
  ...

  //監聽Window對象的事件
  ui.window
    ..onMetricsChanged = handleMetricsChanged
    ..onTextScaleFactorChanged = handleTextScaleFactorChanged
    ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
    ..onSemanticsAction = _handleSemanticsAction;

  //添加PersistentFrameCallback
  addPersistentFrameCallback(_handlePersistentFrameCallback);
}

addPersistentFrameCallback 中添加 _handlePersistentFrameCallback 最終調用了 drawFrame 而 WidgetsBinding 重寫了 RendererBinding 中的 drawFrame() 方法。最終發現我們又回到了 WidgetsBinding 這個類中,在 WidgetsBinding 中 drawFrame 的實現如下:

@override
void drawFrame() {
 ...
  try {
    if (renderViewElement != null)
      // 重構需要更新的element
      buildOwner.buildScope(renderViewElement);
    super.drawFrame(); //調用RendererBinding的drawFrame()方法
    buildOwner.finalizeTree();
  }
}

在上面 scheduleBuildFor 方法介紹中有提到:“scheduleBuildFor 是把一個 element 添加到 _dirtyElements 鏈表,以便當[WidgetsBinding.drawFrame]中調用 buildScope 的時候能夠重構 element。onBuildScheduled()是一個 BuildOwner 的回調”。在 drawFrame 中調用 buildOwner.buildScope(renderViewElement)更新 elements。

  void buildScope(Element context, [ VoidCallback callback ]) {
    ...
      while (index < dirtyCount) {
        assert(_dirtyElements[index] != null);
        assert(_dirtyElements[index]._inDirtyList);
        assert(!_dirtyElements[index]._active || _dirtyElements[index]._debugIsInScope(context));
        try {
          //while 循環進行元素重構
          _dirtyElements[index].rebuild();
        } catch (e, stack) {
        ...
        }
      }
  }

得出

條件判斷

  • 1.生命週期判斷
  • 2.是否安裝mounted

管理類

  • 1.告訴管理類方法自己需要被重新構建
  • 也就是BuildOwner類scheduleBuildFor方法

添加髒鏈表

    1. “髒”鏈表是待更新的鏈表
  • 2.更新過後就不“髒”了
  • 3._active=false 的時候直接返回

調用 window.scheduleFrame()

  • native 方法
  • 按照 Framework 的模型包裝、抽象然後分發
  • WidgetsFlutterBinding 正是粘連 Flutter engine 和上層 Framework 的“膠水”
  • UI 的繪製邏輯是在 Render 樹中實現的

更新幀信號來臨從而刷新需要重構的界面

  • "scheduleBuildFor 是把一個 element 添加到 _dirtyElements 鏈表
  • 以便當[WidgetsBinding.drawFrame]中調用 buildScope 的時候能夠重構 element
  • onBuildScheduled()是一個 BuildOwner 的回調"
  • 在 drawFrame 中調用 buildOwner.buildScope(renderViewElement)更新 elements

圖:

Flutter微信羣

Flutter教程網:www.flutterj.com

Flutter交流QQ羣:874592746

公衆號

關注公衆號“Flutter前線”,各種Flutter項目實戰經驗技巧,幹活知識,Flutter面試題答案,等你來領取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章