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面試題答案,等你來領取。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM