深入浅出 Flutter Framework 之 Layer

语言: CN / TW / HK

本文是『 深入浅出 Flutter Framework 』系列文章的第五篇,

本文同时发表于我的个人博客

本系列文章将深入 Flutter Framework 内部逐步去分析其核心概念和流程,主要包括:

Overview


前面的文章中我们介绍过在 Flutter build、layout、render 过程中会生成 3 棵树:

  • Element Tree
  • RenderObject Tree
  • Layer Tree

可以说 Layer Tree 是 Flutter Framework 最终的输出产物,之后的流程就进入到 Flutter Engine 了。 如上图:

分类


如上图,Layer是抽象基类,其内部实现了基本的 Layer Tree 的管理逻辑以及对渲染结果复用的控制逻辑。 具体的 Layer 大致可以分为2类:

  • Container Layer:正如其名,作为 Layer 容器,用于管理一组 Layers,是唯一可以拥有 child layer 的 Layer;
  • 非 Container Layer:真正用于承载渲染结果的 layer,在 Layer Tree 中属于叶结点,如:PictureLayer承载的是图片的渲染结果,TextureLayer承载的是纹理的渲染结果。

状态管理


抽象基类Layer一个非常重要的职责就是管理 Layer Tree 的状态 简单讲,就是控制什么情况下可以复用 engine 在前一帧渲染的结果,什么情况下需要刷新,即需要 engine 重新渲染。当然,这是出于性能考虑,避免因不必要的渲染操作而浪费资源。

EngineLayer

每个 Layer 实例都有一个与之对应的EngineLayer实例,其属于 engine 层范畴,对 framework 来说是个黑盒。可以简单理解 EngineLayer 为 engine 渲染的结果。

深入浅出 Flutter Framework 之 PaintingContext 一文中介绍过的SceneBuilder类有一系列的push方法 (如:pushOffsetpushClipRect等),这些方法的返回值即为EngineLayer实例。

如:ColorFilterLayer#addToScene方法在调用SceneBuilder#pushColorFilter方法时就保存了其返回的engineLayer

  @override
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    engineLayer = builder.pushColorFilter(colorFilter, oldLayer: _engineLayer);
    addChildrenToScene(builder, layerOffset);
    builder.pop();
  }
复制代码

needsAddToScene

我们知道,Layer上的内容需要借助SceneBuilder生成Scene,之后才能被渲染在屏幕上。 这一过程被称之为addToScene

Layer内有一个非常重要的变量:_needsAddToScene,用于记录该 Layer 自上次渲染后(addToScene)是否发生了变化。 即,该 layer 背后的 EngineLayer 是否可以复用。

Whether this layer has any changes since its last call to [addToScene].

Layer刚初始化时,_needsAddToScenetrue,在第一次调用addToScene后置为false,之后有几种情况可能会被再次置为true,表明该 layer需要 engine 重新渲染,如下图所示:

  • 自身发生了变化,如PictureLayer#pictureTransformLayer#transform被重新赋值;
  • 子节点有增删;
  • 子节点的needsAddToScene变为true

Layer#alwaysNeedsAddToScenetrue时,表示该 layer 在每帧刷新时都需要重新渲染。其对_needsAddToScene的影响如下:

  @protected
  @visibleForTesting
  void updateSubtreeNeedsAddToScene() {
    _needsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;
  }
复制代码

addToScene

addToScene方法可以说是Layer中最重要的方法之一,用于将 layer 送入 engin 进行渲染。 由具体的子类去实现该方法,Container 类型的 Layer 与非 Container 类型的 Layer 在实现上还是有较大区别,细节在此不再赘述,感兴趣的同学可以看看源码。

串起来


如上面这张长长的图,现在要描述的一切都始于RenderView.compositeFrame()

RenderView 是 RenderObject Tree 的根节点,在每帧刷新时都会调用其compositeFrame方法去合成新的帧 RenderView 对应的 Layer 是ContainerLayer

几个关键点:

  • ContainerLayer.buildScene()方法首先去更新needsAddToScene标志位 (对 Layer Tree 进行深度遍历),子节点的值会影响父节点 (子节点有更新时,父节点肯定也要刷新);
  • 之后,调用ContainerLayer.addToScene()方法,该方法会对子节点进行递归操作;
  • 注意,在ContainerLayer._addToSceneWithRetainedRendering()方法中,当_ needsAddToScenefalse_engineLayer!=nil时直接复用上次的渲染结果。

好了,今天就先到这里了!