Flutter的绘制流程简述

相对于React Native、Weex等跨平台框架,Flutter拥有自己的UI绘制体系,避免了React Native、Weex等跨平台框架与Native系统的桥接,从而更好的提升了性能。在Flutter中,UI都是一帧一帧的绘制,但这绘制的背后都会经过如下阶段。动画与微任务阶段,主要是处理动画及执行一系列微任务。构建阶段(build),找出标记为“脏”的节点与布局边界之间的所有节点,...
摘要由CSDN通过智能技术生成

相对于React NativeWeex等跨平台框架,Flutter拥有自己的UI绘制体系,避免了React NativeWeex等跨平台框架与Native系统的桥接,从而更好的提升了性能。

Flutter中,UI都是一帧一帧的绘制,但这绘制的背后都会经过如下阶段。

  1. 动画与微任务阶段,主要是处理动画及执行一系列微任务。
  2. 构建阶段(build),找出标记为“脏”的节点与布局边界之间的所有节点,并做相应的更新。
  3. 布局阶段,计算Widget的大小及位置的确定。
  4. compositingBits阶段,重绘之前的预处理操作,检查RenderObject是否需要重绘。
  5. 绘制阶段,根据Widget大小及位置来绘制UI。
  6. compositing阶段,将UI数据发送给GPU处理。
  7. semantics阶段,与平台的辅助功能相关。
  8. finalization阶段,主要是从Element树中移除无用的Element对象及处理绘制结束回调。

下面就来分析上述的各个阶段

1、动画与微任务阶段

该阶段主要是处理动画及微任务。先来看动画的处理,在使用动画时,很多时候都会添加一个回调函数来进行状态获取或数据更新,如通过addListeneraddStatusListener等函数来添加,而这些回调函数就会在本阶段来执行。具体是在SchedulerBinding中的handleBeginFrame函数中实现。

  void handleBeginFrame(Duration rawTimeStamp) {
    ...
    try {
      // TRANSIENT FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.transientCallbacks;
      //切换为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);
      });
      _removedIds.clear();
    } finally {
      //切换为midFrameMicrotasks阶段
      _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
    }
  }

_invokeFrameCallback就会调用在使用动画时注册的回调函数,这里仅执行一次。如果我们在运行时调用_invokeFrameCallback函数的代码注释调,那么就无法获取动画的状态,从而影响动画的正确执行。

当回调函数执行完毕后,就会进入微任务阶段,在该阶段会执行一系列微任务,由于这涉及到Flutter的异步任务体系,因此这里就不再叙述。

2、build阶段

在上一阶段执行完毕后,就进入build阶段,在该阶段主要是重新构建标记为“脏”的Widget节点及将需要更新的RenderObject对象标记为“脏”。

handleBeginFrame函数执行完毕后,就会执行handleDrawFrame函数,该函数在SchedulerBinding对象初始化时会与Window相关联,所以除第一次需要主动调用外,其他时候皆是通过Window来调用该函数。

  void handleDrawFrame() {
    assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
    Timeline.finishSync(); // end the "Animate" phase
    try {
      //持久帧回调,该回调会一直存在,不会移除
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      for (FrameCallback callback in _persistentCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);

      //当前帧绘制完成回调
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;
      final List<FrameCallback> localPostFrameCallbacks =
          List<FrameCallback>.from(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      //当执行这里时,代表当前帧已经绘制完毕
      for (FrameCallback callback in localPostFrameCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);
    } finally {
      //进入空闲状态
      _schedulerPhase = SchedulerPhase.idle;
      Timeline.finishSync(); // end the Frame
      profile(() {
        _profileFrameStopwatch.stop();
        _profileFramePostEvent();
      });
      _currentFrameTimeStamp = null;
    }
  }

这里重点关注持久帧回调,该回调也是UI绘制的关键函数,是在RendererBinding对象初始化时注册的。

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
  @override
  void initInstances() {
    super.initInstances();
    ...
    //注册持久帧回调
    addPersistentFrameCallback(_handlePersistentFrameCallback);
  }
  void _handlePersistentFrameCallback(Duration timeStamp) {
    //绘制帧
    drawFrame();
  }
  //绘制帧
  void drawFrame() {
    //对Widget进行测量、布局
    pipelineOwner.flushLayout();
    pipelineOwner.flushCompositingBits();
    //对Widget进行绘制
    pipelineOwner.flushPaint();
    //发送数据给GPU
    renderView.compositeFrame(); // this sends the bits to the GPU
    pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
  }
}

根据函数名可以发现并没有发现关于构建Widget的相关函数,那么在何时构建尼?通过查看源码可以发现,在WidgetsBinding中重写了drawFrame函数。在该函数中会创建新的Widget对象替换旧的Widget对象并将不需要的Element节点从树中移除。

  @override
  void drawFrame() {
    ...
    try {
      //如果根结点存在,就重新构建Widget
      if (renderViewElement != null)
        buildOwner.buildScope(renderViewElement);
      //调用RendererBinding中的drawFrame函数
      super.drawFrame();
      //移除不再使用的Element节点
      buildOwner.finalizeTree();
    } finally {...}
    ...
  }
2.1、重新build Widget对象

Widget对象的创建是在buildScope()函数中实现的,这是一个非常重要的函数,具体实现如下。

  void buildScope(Element context, [ VoidCallback callback ]) {
    if (callback == null &a
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值