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 信号的回调,而是直接绘制。

相关文章
相关标签/搜索