深入Flutter渲染更新机制二
接着深入Flutter渲染更新机制一;我们看下父子控件的遍历流程;
相关流程图
父子控件遍历流程图(第一次加载)
Widget,Element,RenderObject三颗树的渲染图
一. ComponentElement 类
1.1 最终发现我们调用performRebuild()方法进行了Widget的构建;在方法内部我们看到我们熟悉的build()方法;并且在finally中将脏数据的标识给重置了;这里我们需要注意的是这个build()是我们返回的当前Widget中的复写的build()返回的子Widget了;
1.2 接下来看下updateChild()方法内部,调用build()方法后会返回子Widget;在调用updateChild方法时会走到[2]这一步,因为_child第一次时肯定是空的;所以会走到inflateWidget()方法内解析子Widget;如果child不为空,则表示已经将子child添加到当前的element上了,就会走到更新的流程了。
1.3 在inflateWidget内部看到了创建了子Widget对应的Element对象;并且调用了mount()方法;在这里是子Element对象调用了mount()方法,并且将当前Element对象this传递给了子Element;我们看下mount()方法;
二 Element类
2.1 Element是一个抽象基类,有不同的子类 ,mount方法也被不同的子类复写;先看下Element内mount()方法的实现;可以看到父Element对象this被传给了_parent;不同的子类,mount()方法实现不样的;主要有二类Element子类 ,一类是主包含单个child的Element, 一类是可以包含多个child的Element;
2.2 单个child的Element的实现
ComponentElement 类
可以看到先调用了父类的mount()方法,然后可以看到方法调用链:_firstBuild()→rebuild()→performRebuild();又回到了调用performRebuild()方法;这样就回到了子子控件的添加流程;
2.3 多个child的Element的实现
MultiChildRenderObjectElement 类
可以看到它对所有的子child进行了遍历inflateWidget;像Row,Column这些可以包含子控件的它们的Element类型都是MultiChildRenderObjectElement
;为什么要这样处理呢?因为这些控件的子child都是平级的关系,没有父子的关系;所以也无法像ComponentElement
这样通过mount()方法完成父子控件的全部遍历了。
三 RenderObject 类
3.1 真正的绘制是通过RenderObject
来完成的,目前为止我们还没看到RenderObject
的身影;
回到[更新机制一中的11.1]中我们看到,在WidgetsBinding
类中的drawFrame()方法中,我们看到buildOwner.buildScope()方法只是将所有脏列表中的数据进行了build();下面的super.drawFrame()才是关键;
3.2 进入super.drawFrame()方法内部
RendererBinding 类
3.3 可以看到布局,绘制等方法的调用;进入方法内部看下详细情况
PipelineOwner 类
可以看到方法内部在对根据进行进行布局的RenderObject进行了遍历布局;最终调用的是performLayout();
3.4 可以看出performLayout()是RenderObject类的一个抽象方法;需要子类去实现;不同的子类实现方法不一样;其实跟Android里面的ViewGroup的原理类似;
RenderObject 类
通过上面的流程,最终会将要绘制的layer
数据发送给GPU去完成绘制;
四. Widget、Element、RenderObject三者的关系
4.1 但这里可以思考一个问题,每个Widget会有一个对应的Element对象,每个Element都会对应一个RenderObject对象嘛?
答案是不一定,我们看下三者部分控件对应的关系
Widget | Element | RenderObject |
---|---|---|
StatelessWidget | StatelessElement | / |
StatefulWidget | StatefulElement | / |
SingleChildRenderObjectWidget | SingleChildRenderObjectElement | RenderObject |
MultiChildRenderObjectWidget | MultiChildRenderObjectElement | RenderObject |
RenderObjectWidget | RenderObjectElement | RenderObject |
从上面表格可以看出StatelessElement,StatefulElement 是没有对应的RenderObject对象的;为什么他们不需要RenderObject对象呢?没有RenderObject那它们是如何去真正的渲染呢?
其实StatelessElement,StatefulElement都是ComponentElement **的子类 ;ComponentElement本身并不直接调用performLayout()方法,并不参与布局操作;它是通过管理子元素的布局来完成自身的布局的。具体的布局操作和performLayout()
方法是在RenderObject
及其子类中完成的。
4.2 Widget、Element、RenderObject三者的关系
Widget | Element | RenderObject | |
---|---|---|---|
定义 | 描述UI组件的配置信息 | 负责管理Widget的生命周期 | 负责渲染UI的具体实现 |
层级关系 | 组成Widget树 | 组成Element树 | 组成RenderObject树 |
状态管理 | 可变 | 可变 | 不可变 |
子节点管理 | 包含子Widget列表 | 包含子Element列表 | 包含子RenderObject列表 |
构建 | 通过build方法创建 | 通过updateChild方法创建 | 通过layout和paint方法创建 |
生命周期 | 无生命周期 | 生命周期管理器负责管理 | 无生命周期 |
渲染 | 无 | 负责协调子Element的渲染 | 负责具体的渲染操作 |
布局 | 无 | 无 | 负责管理子RenderObject的布局 |
绘制 | 无 | 无 | 负责具体的绘制操作 |
上述表格简要描述了Widget、Element和RenderObject之间的关系和特点:
- Widget是描述UI组件的配置信息,可以包含子Widget。它是不可变的,由build方法创建。
- Element是Widget的实例化对象,负责管理Widget的生命周期和管理子Element。它是可变的,通过updateChild方法中创建。
- RenderObject是负责渲染UI的具体实现,是Element的渲染对象。它是不可变的,通过layout和paint方法创建。
Widget树由Widget组成,Element树由Element组成,RenderObject树由RenderObject组成。Element负责协调子Element的渲染,RenderObject负责具体的渲染操作。Widget和Element之前的关系是一对一的关系;Element和RenderObject之间的并不是一对一的关系。
相关文章: