Flutter 生命周期及渲染原理

·  阅读 2353
Flutter 生命周期及渲染原理

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

Widget 生命周期

生命周期的基本概念

我们使用一个对象的时候,有时会需要知道对象的一个状态,什么时候被创建,什么时候被销毁。我们常用的生命周期方法其实本质上就是回调函数,是 Flutter 封装好的,在 Widget 的不同状态设置对应的回调方法给外部使用。

生命周期的作用

  1. 初始化数据
  • 创建变量、常量
  • 发送网络请求
  1. 监听小部件的事件
  2. 管理内存
  • 销毁数据、销毁监听者、定时器等

Widget 常见的生命周期函数

Widget 生命周期函数我们可以分为两种类型来看,无状态与有状态类型。

无状态 Widget (StatelessWidget)

image.png

当无状态 Widget 比渲染的时候会依次调用构造函数与 build 函数。

有状态 Widget (StatefulWidget)

class MyHomePage extends StatefulWidget {
  final String? title;
  MyHomePage({this.title}) {
    print('Widget 构造函数被调用了');
  }
  @override
  _MyHomePageState createState() {
    print('createState 函数被调用了');
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState() {
    print('State 构造函数被调用了');
  }
  @override
  void initState() {
    super.initState();
    print('State 的 init 函数被调用了');
  }
  @override
  Widget build(BuildContext context) {
    print('State build 函数被调用了');
    return Center(child: Text(widget.title ?? ''),);
  }

  @override
  void dispose() {
    super.dispose();
    print('State 的 dispose 函数被调用了');
  }
}

image.png

有状态的 Widget 被渲染的时候会依次调用 StatefulWidget 的构造函数、createState 函数、State 的构造函数、initState 函数、build 函数。当热重载的时候会调用 StatefulWidget 的构造函数跟 Statebuild 函数。当调用 setState 方法的时候会调用 build 函数。

image.png

通过源码可以看到 setState 其实就是 _element 调用了 markNeedsBuild 函数,只是在此之前做了一些错误的判断,这里 _element 就是 context

数据共享部件 InheritedWidget

class MyData extends InheritedWidget {
  final int data; //需要在子组件中共享的数据

  //构造方法
  const MyData({required this.data, required Widget child}) : super(child: child);

  //定义一个便捷方法,方便子组件中的 widget 获取共享数据
  static MyData? ofContext(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType();
  }

  //该回调决定当前 data 发生变化的时候,是否通知子组件(依赖 data 的子组件)
  @override
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {
    print('调用了 updateShouldNotify 函数');
    //如果返回 true,子部件中依赖共享数据的 Widget(build 函数中是否使用共享数据) 的 didChangeDependencies 方法会被调用
    return (oldWidget as MyData).data != data;
  }
}

class InheritedDemo extends StatefulWidget {
  const InheritedDemo({Key? key}) : super(key: key);

  @override
  _InheritedDemoState createState() => _InheritedDemoState();
}

class _InheritedDemoState extends State<InheritedDemo> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return MyData(data: _count, child: Column(
      children: [
        TextDemo(count: _count),
        ElevatedButton(onPressed: () {
          _count++;
          setState(() {});
        }, child: const Icon(Icons.add)),
      ],
    ));
  }
}

class TextDemo extends StatefulWidget {
  final int? count;
  TextDemo({this.count});
  @override
  _TextDemoState createState() => _TextDemoState();
}

class _TextDemoState extends State<TextDemo> {
  @override
  Widget build(BuildContext context) {
    print('调用了 build 函数');
    return Text((MyData.ofContext(context) as MyData).data.toString());
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('调用了 didChangeDependencies 函数');
  }
}

在我们开发的过程中一定会遇到这种场景,子部件的数据需要依赖父部件的数据,当层级比较多的话层层传递的方式就会比较麻烦,所以 Flutter 提供一个 InheritedWidget 部件,用来解决这种场景。如上案例中,我们定义了一个负责数据共享的类 MyData 继承于 InheritedWidgetMyData 的构造方法中有两个参数,data 代表需要共享的数据,child 表示依赖于数据共享的 Widget。并且我们提供了一个 ofContext 方法,供外界获取数据时候使用。使用的话,我们在 _InheritedDemoStatebuild 方法中初始化 MyData,在需要获取共享数据的子部件中通过 MyData.ofContext(context) 来获取数据,这里需要注意的是,子组件需要通过 InheritedWidget 来获取共享数据的话,其根视图部件必须是继承于 InheritedWidget 类的 MyData。当我们执行 _count++ 的时候会调用 updateShouldNotify 方法,在这里我们可以通过返回值来判断是否调用子组件的 didChangeDependencies 方法,类似于发送通知,返回值为 true 的时候就会调用,反之就不调用,我们可以根据需求在 didChangeDependencies 方法中做一些事情。

Flutter 渲染原理

Widget 树、Element 树与 RenderObjc 树

Flutter 中有三个树结构,分别是 Widget 树、Element 树与 RenderObjc 树。Widget 树很直观的就是我们页面所有 Widget 部件的结构关系,但是 Flutter 渲染引擎并不是直接渲染 Widget 树,因为 Widget 是经常发生变化的,直接渲染 Widget 树会非常消耗性能。其实 Flutter 渲染引擎渲染的是 RenderObjc 树,RenderObjc 树中每个节点是一个又一个的 RenderObjc 对象,但并不是所有的 Widget 都会生成 RenderObjc,只有 RenderObjcWidget 的子类才会生成 RenderObjc 对象,才会被渲染引擎直接渲染。

abstract class RenderObjectWidget extends Widget {
  RenderObjectElement createElement();
  RenderObject createRenderObject(BuildContext context);
}

RenderObjectWidget 类中我们需要关注 createElementcreateRenderObject 这两个方法,这两个方法都是抽象方法,所有在子类中应该有相应的实现,这里我们先来看 createRenderObject 这个方法。

class Flex extends MultiChildRenderObjectWidget {
  @override
  RenderFlex createRenderObject(BuildContext context) {
    return RenderFlex(
      direction: direction,
      mainAxisAlignment: mainAxisAlignment,
      mainAxisSize: mainAxisSize,
      crossAxisAlignment: crossAxisAlignment,
      textDirection: getEffectiveTextDirection(context),
      verticalDirection: verticalDirection,
      textBaseline: textBaseline,
      clipBehavior: clipBehavior,
    );
  }
}

RenderObjectWidget 的子类 Flex 中我们可以看到 createRenderObject 其实就是返回一个继承于 RenderObject 的子类对象 RenderFlex,并加入到 RenderObject 树中。所以只有继承于 RenderObjectWidget 的子类且实现 createRenderObject 方法的部件才能被独立渲染。

Element 树

abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
  MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);
}
class MultiChildRenderObjectElement extends RenderObjectElement {
  MultiChildRenderObjectElement(MultiChildRenderObjectWidget widget)
    : assert(!debugChildrenHaveDuplicateKeys(widget, widget.children)),
      super(widget);
  }

RenderObjectWidget 类中除了 createRenderObject 方法还有 createElement 方法。通过源码可以看到 RenderObjectWidget 的子类 MultiChildRenderObjectWidget 中实现了 createElement 方法,会创建一个继承于 RenderObjectElement 的子类 MultiChildRenderObjectElement 对象。

所以继承于 RenderObjectWidget 的子类除了创建 RenderObject,还会创建 Element,三个树都有。

下面我们看一下不继承于 RenderObjectWidget 的部件。

abstract class StatelessWidget extends Widget {
  StatelessElement createElement() => StatelessElement(this);
}
abstract class StatefulWidget extends Widget {
  StatefulElement createElement() => StatefulElement(this);
}

这里可以看到继承于 Widget 的子类都会实现 createElement 方法,都会创建 Element。所以可以得出结论,所有 Widget 都会创建 Element 对象,Widget 都会有对应的 Element 对象。

image.png

通过源码注释可以看到,只要有新的 Element 被添加都会调用 mount 方法。也就是说有新的 Widget 被创建的时候都会调用一次 mount 方法,因为 WidgetElement 是一一对应的关系。

  @override
  void mount(Element? parent, Object? newSlot) {
    super.mount(parent, newSlot);
    assert(() {
      _debugDoingBuild = true;
      return true;
    }());
    _renderObject = widget.createRenderObject(this);
}

如果是普通的 Element 调用完 mount 方法之后就结束了,但是继承于 RenderObjectElement 的子类在 mount 方法中会调用 createRenderObject 方法,创建 RenderObject 对象。

StatelessWidget 的 Element

abstract class Widget extends DiagnosticableTree {
  Element createElement();
}

以上只附上了关键代码,通过源码我们可以看到 Widget 类都会实现 createElement 函数。这里我们先看下 StatelessWidget 类与 Element 的关系。

abstract class StatelessWidget extends Widget {
  StatelessElement createElement() => StatelessElement(this);
}

StatelessWidgetcreateElement 方法会创建一个 StatelessElement 对象并加入到 Elment 树中,并且返回 StatelessElement 对象,创建 StatelessElement 对象的时候 StatelessWidget 自己作为参数传给 StatelessElement 对象。

class StatelessElement extends ComponentElement {
  Widget build() => widget.build(this);
}
abstract class ComponentElement extends Element {
  void mount(Element? parent, Object? newSlot) {
    super.mount(parent, newSlot);
    assert(_child == null);
    assert(_lifecycleState == _ElementLifecycle.active);
    _firstBuild();
    assert(_child != null);
  }
abstract class Element extends DiagnosticableTree implements BuildContext {
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

通过源码追踪可以看到 StatelessElement 继承于 ComponentElementComponentElement 继承于 Element,在 Element 的构造方法中外部传入的 widget 会被赋值给 _widget 属性,在 ComponentElement 中会调用 mount 方法, mount 方法中的 _firstBuild 最终会执行 ComponentElementbuild 方法,并且 StatelessElementbuild 会执行 widget.build(this),也就是调用 Widgetbuild 方法,如下代码所示,并把自己传给外面。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {}
}

所以我们外部 StatelessWidget 子类中的 context 就是 Element 对象。

StatefulWidget 的 Element

abstract class StatefulWidget extends Widget {
  @override
  StatefulElement createElement() => StatefulElement(this);
}

StatefulWidget 同样会执行 createElement,但是返回的对象是 StatefulElement 类型。

class StatefulElement extends ComponentElement {
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
        state._element = this;
        state._widget = widget;
}

  @override
  Widget build() => state.build(this);
}

但是 StatefulElement 多了一步就是执行 createState 函数,并且赋值给 _state 属性,并且把外部传入的 widget 赋值给 state._widget 属性,这也是我们能在 state 中能获取到 widget 的原因。并且会把 this 指针赋值给 state._element ,这里 build 方法中执行的是 state.build(this)

分类:
iOS
标签:
分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改