Flutter的setState更新原理和流程

本文來自整理和簡化面試

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

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

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

_element.markNeedsBuild();

那咱們看看markNeedsBuild。ide

Element 類 markNeedsBuild方法

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

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

  • 這裏的「 」鏈表是待更新的鏈表,更新事後就不「髒」了。
  • 因爲一幀作兩次更新有點低效,因此在_active=false 的時候直接返回。

那咱們看看本方法最後調用的scheduleBuildFor方法。動畫

BuildOwner 類 scheduleBuildFor方法

BuildOwner類是widget framework的管理類,它跟蹤那些須要從新構建的 widget。ui

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 的回調。this

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

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方法:spa

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

相關文章
相關標籤/搜索