Flutter Framework 渲染流程分析(一):开篇
在 Flutter 的渲染系统中,Framework 层首先会根据页面定义的元素生成一个个由 Layer 组成的
Scene
对象,接着ui.window
会将这些渲染信息传递到 Engine 层,Engine 层调用底层渲染引擎的接口将这些像素信息绘制到屏幕上。
导读
对于开发者而言,Framework 层渲染是最直接接触到的,了解其中的原理可辅助我们定位问题,写出更高效的代码。
Widget
![](https://img.haomeiwen.com/i27820348/18a9803d17922012.png)
Widget 可以分为三类对象。一类是绘制类
,对应RenderObjectWidget
,负责布局和绘制,渲染流程中的Layout
、Paint
阶段就与其紧密相关;一类是布局类
,对应StatelessWidget
和StatefulWidget
,它更像是一个组织者,通过组合各种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);
通过这些方法,可以得出:
-
Element
和RenderObject
都是从Widget
产生的(通过相应的create
方法); - 所有
Widget
都对应于一个Element
,但只有RenderObjectWidget
才有对应的RenderObject
; - 有两个
build
方法,一个是StatelessWidget
的,另外一个是State
的。但间接或直接都归属于布局类
,毕竟像上面说的只有布局类
才会包含子组件并进行组合。
Element
![](https://img.haomeiwen.com/i27820348/acb81b235ef226b0.png)
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
持有widget
和renderObject
对象,这很重要,我们常说的三棵树的形成就与其息息相关。 事实上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 widget
的createElement
方法得到child element
,然后child element
又会执行mount
方法将自己挂载到这棵树上,mount
行为便形成一个循环的操作。
AbstractNode
我们将在第三、四篇介绍的RenderObject
、Layer
的基类都是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
的特性depth
、owner
、parent
在Element
都有同名属性存在,但由于Element
继承了一个用于记录节点信息的类DiagnosticableTree
,而dart
做不到多继承。事实上我认为Element
理应也继承自AbstractNode
,毕竟同样身为树结构,只不过需要另外用Mixin
处理一下跟DiagnosticableTree
的关系即可。
总结
作为开篇引言,本文简单的讲了Widget
、Element
、AbstractNode
这几个类,通过类的关键属性和方法介绍了它的作用。关于Element
中树的构建,在下个章节中会详细介绍,我们常说的Widget Tree
、Element Tree
、RenderObject Tree
其实都是间接通过Element
构建过程中形成的。