深入Flutter渲染更新机制二

1,151 阅读5分钟

深入Flutter渲染更新机制二

接着深入Flutter渲染更新机制一;我们看下父子控件的遍历流程;

相关流程图

父子控件遍历流程图(第一次加载)

Untitled.png

Widget,Element,RenderObject三颗树的渲染图

WidgetElementRenderObject_(2).png

一. ComponentElement 类

1.1 最终发现我们调用performRebuild()方法进行了Widget的构建;在方法内部我们看到我们熟悉的build()方法;并且在finally中将脏数据的标识给重置了;这里我们需要注意的是这个build()是我们返回的当前Widget中的复写的build()返回的子Widget了;

Untitled 1.png

1.2 接下来看下updateChild()方法内部,调用build()方法后会返回子Widget;在调用updateChild方法时会走到[2]这一步,因为_child第一次时肯定是空的;所以会走到inflateWidget()方法内解析子Widget;如果child不为空,则表示已经将子child添加到当前的element上了,就会走到更新的流程了。

Untitled 2.png

1.3 在inflateWidget内部看到了创建了子Widget对应的Element对象;并且调用了mount()方法;在这里是子Element对象调用了mount()方法,并且将当前Element对象this传递给了子Element;我们看下mount()方法;

Untitled 3.png

二 Element类

2.1 Element是一个抽象基类,有不同的子类 ,mount方法也被不同的子类复写;先看下Element内mount()方法的实现;可以看到父Element对象this被传给了_parent;不同的子类,mount()方法实现不样的;主要有二类Element子类 ,一类是主包含单个child的Element, 一类是可以包含多个child的Element;

Untitled 4.png

2.2 单个child的Element的实现

ComponentElement 类

可以看到先调用了父类的mount()方法,然后可以看到方法调用链:_firstBuild()→rebuild()→performRebuild();又回到了调用performRebuild()方法;这样就回到了子子控件的添加流程;

Untitled 5.png

2.3 多个child的Element的实现

MultiChildRenderObjectElement 类

Untitled 6.png

可以看到它对所有的子child进行了遍历inflateWidget;像Row,Column这些可以包含子控件的它们的Element类型都是MultiChildRenderObjectElement ;为什么要这样处理呢?因为这些控件的子child都是平级的关系,没有父子的关系;所以也无法像ComponentElement 这样通过mount()方法完成父子控件的全部遍历了。

三 RenderObject 类

3.1 真正的绘制是通过RenderObject来完成的,目前为止我们还没看到RenderObject 的身影;

回到[更新机制一中的11.1]中我们看到,在WidgetsBinding 类中的drawFrame()方法中,我们看到buildOwner.buildScope()方法只是将所有脏列表中的数据进行了build();下面的super.drawFrame()才是关键;

Untitled 7.png

3.2 进入super.drawFrame()方法内部

RendererBinding 类

Untitled 8.png

3.3 可以看到布局,绘制等方法的调用;进入方法内部看下详细情况

PipelineOwner 类

可以看到方法内部在对根据进行进行布局的RenderObject进行了遍历布局;最终调用的是performLayout();

Untitled 9.png

3.4 可以看出performLayout()是RenderObject类的一个抽象方法;需要子类去实现;不同的子类实现方法不一样;其实跟Android里面的ViewGroup的原理类似;

RenderObject 类

Untitled 10.png

Untitled 11.png

通过上面的流程,最终会将要绘制的layer 数据发送给GPU去完成绘制;

四. Widget、Element、RenderObject三者的关系

4.1 但这里可以思考一个问题,每个Widget会有一个对应的Element对象,每个Element都会对应一个RenderObject对象嘛?

答案是不一定,我们看下三者部分控件对应的关系

WidgetElementRenderObject
StatelessWidgetStatelessElement/
StatefulWidgetStatefulElement/
SingleChildRenderObjectWidgetSingleChildRenderObjectElementRenderObject
MultiChildRenderObjectWidgetMultiChildRenderObjectElementRenderObject
RenderObjectWidgetRenderObjectElementRenderObject

从上面表格可以看出StatelessElement,StatefulElement 是没有对应的RenderObject对象的;为什么他们不需要RenderObject对象呢?没有RenderObject那它们是如何去真正的渲染呢?

其实StatelessElement,StatefulElement都是ComponentElement **的子类 ;ComponentElement本身并不直接调用performLayout()方法,并不参与布局操作;它是通过管理子元素的布局来完成自身的布局的。具体的布局操作和performLayout()方法是在RenderObject及其子类中完成的。

4.2 Widget、Element、RenderObject三者的关系

WidgetElementRenderObject
定义描述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之间的并不是一对一的关系。

相关文章:

  1. 深入Flutter渲染更新机制一
  2. 深入Flutter渲染更新机制二
  3. 深入Flutter渲染-Android端
  4. 深入理解StatefulWidget生命周期