3、Flutter UI 更新流程

1、Flutter 之圖像繪製原理bash

2、Widget、Element、RenderObjectapp

4、build 流程分析ide

5、layout 流程分析函數

6、Paint 繪製(1)佈局

7、Paint 繪製(2)post

8、composite 流程分析動畫

9、Flutter 小實踐ui

第一章說起到在 Flutter 中,是基於 Vsync 垂直信號的機制來協調圖像數據的生成,那在 flutter 中,是怎麼觸發監聽 vsync 信號的呢this

一、Vsync 註冊監聽

setState 方法 是咱們在寫UI 組件時經常使用的方法,用於UI界面數據的更新spa

(1)調用 setState 方法

@protected
void setState(VoidCallback fn) {
  _element.markNeedsBuild();
}
複製代碼

(2)markNeedsBuild

void markNeedsBuild() {
    if (dirty)
      return;
    _dirty = true;  // 將元素標記爲"髒元素"
    owner.scheduleBuildFor(this);
}
複製代碼

將元素標記爲"髒元素"( flutter 在更新觸發從新渲染時,只會將標髒了的元素從新繪製渲)同時調用 BuildOwner 的 scheduleBuildFor 方法

(3)scheduleBuildFor

void scheduleBuildFor(Element element) {
  if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
    _scheduledFlushDirtyElements = true;
    onBuildScheduled();
  }
  _dirtyElements.add(element);
  element._inDirtyList = true;
}
複製代碼

這個方法中,將該元素添加到髒元素集合中,同時調用 onBuildScheduled 方法

(4) onBuildScheduled

mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    buildOwner.onBuildScheduled = _handleBuildScheduled;
  }
複製代碼

onBuildScheduled 方法是在 flutter 初始化時進行綁定的一個方法

(5) _handleBuildScheduled

void _handleBuildScheduled() {
  ensureVisualUpdate();
}
複製代碼

(6) ensureVisualUpdate判斷當前調度所處的狀態,若是是 idle(空閒)或 postFrameCallbacks 時調用 scheduleFrame

void ensureVisualUpdate() {
  switch (schedulerPhase) {
    case SchedulerPhase.idle:
    case SchedulerPhase.postFrameCallbacks:
      scheduleFrame();
      return;
    case SchedulerPhase.transientCallbacks:
    case SchedulerPhase.midFrameMicrotasks:
    case SchedulerPhase.persistentCallbacks:
      return;
  }
}
複製代碼

(7) scheduleFrame

void scheduleFrame() {
  window.scheduleFrame();
}
複製代碼

(8)window.scheduleFrame

void scheduleFrame() native 'Window_scheduleFrame';
複製代碼

scheduleFrame 是底部 dart engine 底層的一個方法,這個方法用於 註冊 vsync 信號的監聽。因而可知,setState 方法的主要原理是,將當前元素標髒,同時觸發 vsync 信號,以便在下次 vsync 信號回調時,完成這些髒元素的更新。

二、Vsync 信號回調

(1) Window

Window Flutter Framework鏈接宿主操做系統的接口, 在 Window 類 中定義了Vsync 信號的回調處理

VoidCallback get onDrawFrame => _onDrawFrame;
複製代碼

(2) SchedulerBinding

window.onDrawFrame = _handleDrawFrame;
複製代碼

onDrawFrame 方法在 SchedulerBinding初始化時進行了從新指向

(3) _handleDrawFrame

void _handleDrawFrame() {
  handleDrawFrame();
}
複製代碼

(4) handleDrawFrame

void handleDrawFrame() {
    _schedulerPhase = SchedulerPhase.persistentCallbacks;
    for (FrameCallback callback in _persistentCallbacks) {
      _invokeFrameCallback(callback, _currentFrameTimeStamp);
    }
    // POST-FRAME CALLBACKS
    _schedulerPhase = SchedulerPhase.postFrameCallbacks;
    final List<FrameCallback> localPostFrameCallbacks =
        List<FrameCallback>.from(_postFrameCallbacks);
    _postFrameCallbacks.clear();
    for (FrameCallback callback in localPostFrameCallbacks)
      _invokeFrameCallback(callback, _currentFrameTimeStamp);
}
複製代碼

在這個方法中,主要是執行一些回調集合,其中

persistentCallbacks:用於存放一些持久的回調,以下代碼所示SchedulerBinding.instance.addPersitentFrameCallback(),這個回調中處理了佈局與繪製工做,即 調用執行 build/layout/paint 流水線工做的地方,

postFrameCallbacks:在Frame結束時只會被調用一次,調用後會被系統移除,可由 SchedulerBinding.instance.addPostFrameCallback() 註冊

(5)persistentCallbacks --> RendererBinding

void initInstances() {
  super.initInstances();
  addPersistentFrameCallback(_handlePersistentFrameCallback);
}
複製代碼

(6)_handlePersistentFrameCallback

void _handlePersistentFrameCallback(Duration timeStamp) {
  drawFrame();
}
複製代碼

(7)WidgetsBinding 中的 drawFrame

void drawFrame() {
  try {

    if (renderViewElement != null)
      buildOwner.buildScope(renderViewElement);
    super.drawFrame();
    buildOwner.finalizeTree();
  }
}
複製代碼

(8) super.drawFrame()(RendererBinding)

void drawFrame() {
  pipelineOwner.flushLayout();
  pipelineOwner.flushCompositingBits();
  pipelineOwner.flushPaint();
  renderView.compositeFrame(); // this sends the bits to the GPU
  pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
}
複製代碼

mixin widgetBinding 以 RenderingBinding 爲基礎,所以會覆蓋 RendererBinding 的drawFrame 函數,該方法執行的操做,其實就是 build -> layout -> paint -> composite

三、首幀渲染

前面分析了 setState 方法的工做原理,該方法主要是更新數據時調用,但 app 啓動過程當中,並無觸發 setState 方法,整個 app 首幀是怎麼渲染的呢?

(1) runApp

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..scheduleWarmUpFrame();
}
複製代碼

ensureInitialized 主要時實現 各類 Binding 的初始化,例如 SchedulerBinding, RendererBinding, WidgetsBinding 等, attachRootWidget 方法是完成整個 widget UI 樹的構建掛載

(2)scheduleWarmUpFrame

void scheduleWarmUpFrame() {
    handleBeginFrame(null);
    handleDrawFrame();
    if (hadScheduledFrame)
      scheduleFrame();
}
複製代碼

在完成UI 樹的掛載以後,調用了 scheduleWarmUpFrame, 這個方法主要是調用了 handleBeginFrame 和 handleDrawFrame, 其中 handleBeginFrame 主要是 處理 transientCallbacks, 這個方法通常存放動畫回調。能夠經過SchedulerBinding.instance.scheduleFrameCallback 添加回調, handleDrawFrame 方法則是調用 drawFrame 方法完成首幀的繪製

(3) handleBeginFrame

void handleBeginFrame(Duration rawTimeStamp) {
 
 
    _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);
    });
}
複製代碼

因而可知,首幀的繪製並無等待 Vsync 信號的回調,而是直接繪製。

相關文章
相關標籤/搜索