前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Widget的生命周期和渲染原理

Widget的生命周期和渲染原理

作者头像
拉维
发布2022-03-28 09:04:23
1.2K0
发布2022-03-28 09:04:23
举报
文章被收录于专栏:iOS小生活iOS小生活

Widget的生命周期

关于生命周期,我之前写过一篇文章总结过:提到生命周期,我们是在说什么?今天这个篇幅是以此文章为基准,再做一些补充。

其实,所谓的生命周期,就是一系列的方法回调,我们可以通过实现这些方法来捕获一个widget从加载到卸载全过程中的各个节点,以在合适的时机做合适的事情。

那么我们可以利用生命周期方法做哪些事情呢?我下面可以稍微罗列一下:

  • 内存管理相关,比如销毁数据、销毁监听者、销毁timer
  • 初始化数据,比如发送网络请求,创建一些数据等

一般而言,Flutter的widget分为StatefulWidget和StatelessWidget,接下来分别作介绍。

StatelessWidget的生命周期

1,初始化构造方法

2,widget的build函数

StatefulWidget的生命周期

1,statefulWidget的构造函数

2,createState

3,对应State的构造函数

4,对应State的初始化函数initState

5,didChangeDependencies

详见《使用InheritedWidget来进行状态管理

6,state的build函数

上面👆第5步中的didChangeDependencies会触发state的build()函数

我们知道,在需要修改数据更新UI的时候,只要调用setState然后在其中更改数据,这样UI就可以随之改变了,这是因为setState函数可以触发widget的销毁重建,也就是会触发state的build函数的重新调用

接下来我们看一下setState的源码:

可以看到,除了断言,这里面实际上就调用了一行代码:

_element!.markNeedsBuild();

这会给element标记为需要重建,然后element对应的widget就会销毁重建。

这里说句题外话,其实这里的_element就是我们在业务代码中常见的context,如何证明这一点呢?我们点击上面的_element,就来到了其定义的地方,然后有如下代码:

可以看到context的getter里面返回的就是_element。

好,现在我们知道了通过setState来根据数据自动调整UI的原理了,因此,原则上我们是可以不调用setState而直接给element调用markNeedsBuild函数来实现UI的更新,即:

在StatefulWidget的build方法中将context转成StatefulElement类型的element,然后直接在对应的数据更新完了之后,手动调用element.markNeedsBuild(),这样就能够实现UI的更新了。但我们开发的时候不会这样去用,因为setState里面做了很多assert断言的容错判断,会更加安全。

7,deactive

当State对象被从视图渲染树中移除的时候,就会调用state的deactive。比如当某个StatefulWidget的可见状态发生了变化,此时该widget对应的state会被暂时从视图渲染树中移除(后面还会用,并未销毁哦),因此就会调用deactive;再比如当视图切换的时候,由于State对象在视图渲染树中的位置发生了变化,因此需要暂时移除之后再重新添加,此时就会触发deactive。

下面👇这张图就展示了当视图切换(push、pop)的时候,各个生命周期函数的调用情况

可以看到,deactive是可以被调用多次的。

8,dispose

当State对象被永久地从视图树中移除时,Flutter会调用dispose函数。而一旦到这个阶段,组件就要被销毁了,所以我们可以在这里进行最终的资源释放、移除监听、清理环境,等等

Widget的渲染原理

关于Widget的渲染,我在Widget,构建Flutter界面的基石中有过介绍,本文也是依次为基准,再做一些拓展介绍。

截至目前,我接触到的直接继承自Widget的类有三个,分别是:StatefulWidget、StatelessWidget和RenderObjectWidget:

abstract class StatefulWidget extends Widget

abstract class StatelessWidget extends Widget

abstract class RenderObjectWidget extends Widget

并不是所有的widget都会有对应的一个RenderObject对象(也就是说,并非所有的widget都会被独立渲染),只有直接或者间接继承自RenderObjectWidget的widget才会最后生成一个对应的RenderObject,然后将其加入到渲染树中进行渲染

但是,所有的widget都会创建一个对应的Element对象

上面分别列出了StatelessWidget、StatefulWidget和RenderObjectWidget的源码,从源码中也可以看出,三者都有createElement()函数,这也进一步说明了,所有的widget都会创建一个对应的Element对象

那么createElement()函数啥时候被调用呢?实际上,当创建完了widget之后,Flutter Framework就会去隐式调用createElement()函数来创建对应的Element,创建完了对应的Element后就会将该element加入到Element树当中,一旦将Element对象加入到了Element树当中,Flutter Framework就会去调用对应Elemen中的mount方法。接下来我们就来分别研究一下StatelessWidget、StatefulWidget和RenderObjectWidget的createElement()函数。

StatelessWidget的createElement()

可以看到,StatelessWidget中的createElement()函数返回的是一个StatelessElement类型的对象。并且这里的this代表的是当前的这个StatelessWidget。

当创建了一个StatelessWidget之后,Flutter Framework必然会调用StatelessWidget的createElement创建StatelessElement对象并将其加入到Element树当中,之后Flutter Framework会接着调用ComponentElement的mount函数

接下来看一下StatelessElement的实现:

可以看到,StatelessElement继承自ComponentElement

接下来看下ComponentElement的源码:

可以看到,在ComponentElement的mount函数中,除去断言之外,只做了一件事情,就是调用_firstBuild();,然后我们一层一层点进去:

void _firstBuild() {
    rebuild();
  }

此时,再点performRebuild就点不进去了,这个时候将鼠标定位到这里,然后Command + Option + B,如下:

然后点进去:

可以看到,这里面调用了build()函数。

以上分析得出结论如下:

StatelessElement中的mount函数经过一系列的方法跳转,最终会取出对应的StatelessWidget来调用其build函数

StatefulWidget的createElement()

可以看到,StatefulWidget中的createElement()函数返回的是一个StatefulElement类型的对象。并且这里的this代表的是当前的这个StatefulWidget。

接下来看一下StatefulElement的实现:

可以看到,StatefulElement继承自ComponentElement

当创建了一个StatefulWidget之后,Flutter Framework必然会调用StatefulWidget的createElement创建StatefulElement对象并将其加入到Element树当中,之后Flutter Framework会接着调用StatefulElement的mount函数

ComponentElement的源码在上面已经展示过了,关于mount函数的分析与上面等同。

但是这里讲一个StatefulElement和StatelessElement不同的地方,如下:

可以看到,在创建StatefulElement的时候,比创建StatelessElement多做了两件事情:

  1. 调用widget中的createState函数创建State对象;
  2. 将widget和element赋值给State对象的相关属性,这样就可以在StatefulWidget对应的State里面获取到element和widget了。

接下来总结一下StatefulWidget的渲染流程:

创建完一个StatefulWidget之后,Flutter Frame会调用StatefulWidget的createElement()函数,在该函数中会创建一个StatefulElement;

在StatefulElement的构建函数中,调用了widget的createState函数来创建State,并且给创建出来的State对象的element和wiget进行赋值传递;

创建完StatefulElement后会将该element插入到element树当中,然后Flutter Framework会调用ComponentElement的mount函数,在mount函数中会调用widget的build函数

RenderObjectWidget的createElement()

可以看到,RenderObjectWidget中的createElement()函数返回的是一个RenderObjectElement类型的对象。

由于RenderObjectWidget是一个抽象接口类,所以createElement()函数需要在其子类中实现,我们这里以它的一个子类进行演示:

可以看到,在通过createElement创建Element的时候也传入了一个this,这个this就是对应的RenderObjectWidget。

当创建了一个Widget之后,Flutter Framework必然会调用createElement创建Element对象并将其加入到Element树当中,之后Flutter Framework会接着调用Element的mount函数

接下来看下RenderObjectElement的源码:

除去一堆的断言之后,RenderObjectElement中的mount函数源码如下:

  @override
  void mount(Element? parent, Object? newSlot) {
    super.mount(parent, newSlot);
    _renderObject = widget.createRenderObject(this);
    attachRenderObject(newSlot);
    _dirty = false;
  }

可以看到,在RenderObjectElement中的mount函数中,会完成与之关联的RenderObject对象的创建,以及渲染树的插入工作。

需要注意的是,是通过RenderObjectWidget中的createRenderObject函数来创建RenderObject的;而且这里传入的this是RenderObjectElement对象,这样就可以将Element和RenderObject对应起来了。

以上。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-02-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS小生活 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档