本文來自整理和簡化面試
調用 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方法
添加髒鏈表
-
- 「髒」鏈表是待更新的鏈表
- 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面試題答案,等你來領取。