likes
comments
collection
share

Flutter Framework 渲染流程分析(一):开篇

作者站长头像
站长
· 阅读数 16

在 Flutter 的渲染系统中,Framework 层首先会根据页面定义的元素生成一个个由 Layer 组成的Scene对象,接着ui.window会将这些渲染信息传递到 Engine 层,Engine 层调用底层渲染引擎的接口将这些像素信息绘制到屏幕上。

导读

对于开发者而言,Framework 层渲染是最直接接触到的,了解其中的原理可辅助我们定位问题,写出更高效的代码。这个系列文章将会围绕渲染流程做深入解析,分为以下几部分:

  • Flutter Framework 渲染流程分析(一):开篇
  • Flutter Framework 渲染流程分析(二):Element
  • Flutter Framework 渲染流程分析(三):RenderObject
  • Flutter Framework 渲染流程分析(四):Layer
  • Flutter Framework 渲染流程分析(五):常见问题分析

Widget

Flutter Framework 渲染流程分析(一):开篇

Widget 可以分为三类对象。一类是绘制类,对应RenderObjectWidget,负责布局和绘制,渲染流程中的LayoutPaint阶段就与其紧密相关;一类是布局类,对应StatelessWidgetStatefulWidget,它更像是一个组织者,通过组合各种RenderObjectWidget来丰富页面的布局;一类是代理类,对应ProxyWidget,它能向子节点提供额外的数据,方便我们开发时在 Widget Tree 之间传递数据。

绘制类是核心的功能模块,布局类将这些模块进行堆叠,而代理类则是在这些模块之间传递数据。

Widget 的关键方法

// code 1.1
// Note: [] 内是类名,后面是方法名。下同!

[Widget] Element createElement();

[Widget] static bool canUpdate(Widget oldWidget, Widget newWidget) {...};

[StatelessWidget] Widget build(BuildContext context);

[StatefulWidget] State createState();

[State] Widget build(BuildContext context);

[RenderObjectWidget] Renderobject createRenderObject(BuildContext context);

通过这些方法,可以得出:

  1. ElementRenderObject都是从Widget产生的(通过相应的create方法);
  2. 所有 Widget 都对应于一个 Element,但只有RenderObjectWidget 才有对应的 RenderObject
  3. 有两个build方法,一个是StatelessWidget的,另外一个是State的。但间接或直接都归属于布局类,毕竟像上面说的只有布局类才会包含子组件并进行组合。

Element

Flutter Framework 渲染流程分析(一):开篇

Element 简化了一下分类。一种是绘制类,也就是RenderObjectElement;另一种是组件类,也就是ComponentElement,它囊括了上述Widget中的布局类代理类

Element 的关键属性

// code 1.2

[Element] Widget? _widget;

[Element] RenderObject? renderObject;

[Element] Element? _parent;

[Element] Object? _slot;

[ComponentElement] Element? _child;

[SingleChildRenderObjectElement] Element? _child;

[MultiChildRenderObjectElement] List<Element> _children;

Element持有widgetrenderObject对象,这很重要,我们常说的三棵树的形成就与其息息相关。 事实上Element在渲染流程中的build阶段都是至关重要的。同时,它也持有 _parent element_child element,形成一个双向链表的数据结构。

Element 的关键方法

// code 1.3

// 1. 挂载操作。将自己挂载到树上。
[Element] void mount(Element? parent, Object? newSlot)

// 2. 构建操作。 
[ComponentElement] void _firstBuild()

[Element] void rebuild({bool force = false});

[Element] void performRebuild()

[RenderObjectElement] void _performRebuild()

// 3. 更新元素。执行更新或者重新新建
[Element] Element? updateChild(Element? child, Widget? newWidget, Object? newSlot);

[Element] void update(covariant Widget newWidget)

[Element] Elment inflateWidget(Widget newWidget, Object? newSlot);

Element通过以上这些方法形成一棵树。它在挂载(mount)自身的时候会触发构建(performRebuild)行为,在构建的过程中会通过前面提到的[Widget]build()方法得到child widget,经过 updateChild最终会走到加载(inflateWidget)inflateWidget方法通过child widgetcreateElement方法得到child element,然后child element又会执行mount方法将自己挂载到这棵树上,mount行为便形成一个循环的操作。

AbstractNode

我们将在第三、四篇介绍的RenderObjectLayer的基类都是AbstractNode,用来表示一棵树上的抽象节点。

// code 1.4

// 1. 深度。节点的深度标志和更新
[AbstractNode] int get depth => _depth;

[AbstractNode] int _depth = 0;

[AbstractNode] void redepthChild(AbstractNode child) {}

[AbstractNode] void redepthChildren() { }

// 2. 持有。节点的持有者,通常情况下所有节点的被同一个 owner 持有。
[AbstractNode] Object? get owner => _owner;

[AbstractNode] Object? _owner;

[AbstractNode] bool get attached => _owner != null;

[AbstractNode] void attach(covariant Object owner) {}

[AbstractNode] void detach() {}

// 3. 父节点。插入节点或移除节点时会对父节点进行更新。
[AbstractNode] AbstractNode? get parent => _parent;

[AbstractNode] AbstractNode? _parent;

[AbstractNode] void adoptChild(covariant AbstractNode child) {}

[AbstractNode] void dropChild(covariant AbstractNode child) {}

根据以上接口,将一个节点抽象出了三个特性:深度持有者父节点。通过_parent,便形成了一个树结构。

为什么Element不继承自AbstractNode?上面我们说过,Element也是一个树结构,AbstractNode的特性depthownerparentElement都有同名属性存在,但由于Element继承了一个用于记录节点信息的类DiagnosticableTree,而dart做不到多继承。事实上我认为Element理应也继承自AbstractNode,毕竟同样身为树结构,只不过需要另外用Mixin处理一下跟DiagnosticableTree的关系即可。

总结

作为开篇引言,本文简单的讲了WidgetElementAbstractNode这几个类,通过类的关键属性和方法介绍了它的作用。关于Element中树的构建,在下个章节中会详细介绍,我们常说的Widget TreeElement TreeRenderObject Tree其实都是间接通过Element构建过程中形成的。