Flutter - 初探渲染原理

作者: Lcr111 | 来源:发表于2021-12-24 17:50 被阅读0次

    前言

    讲到Flutter的渲染原理,就离不开Flutter中的三棵树(Widget TreeElement TreeRender Tree)。
    当应用启动时 Flutter 会遍历并创建所有的 Widget 形成 Widget Tree,同时与 Widget Tree 相对应,通过调用 Widget 上的 createElement()方法创建每个 Element 对象,形成 Element Tree
    如果是继承自RenderObjectWidget的Widget,最后调用 Element 的 createRenderObject() 方法创建每个渲染对象,形成一个 Render Tree
    并不是所有的Widget都会被独立渲染,只有继承RenderObjectWidget的才会创建RenderObject对象!也就组成了Render Tree!

    Widget

    正如 Flutter 的口号 Everything’s a widget,万物皆Widget。一个Flutter应用能正常运行,展示出来的UI就是由一个个Widget搭建构成的。

    @immutable
    abstract class Widget extends DiagnosticableTree {
      /// Initializes [key] for subclasses.
      const Widget({ this.key });
      final Key? key;
    
      @protected
      @factory
      Element createElement();
      /// Whether the `newWidget` can be used to update an [Element] that currently
      /// has the `oldWidget` as its configuration.
      ///
      /// An element that uses a given widget as its configuration can be updated to
      /// use another widget as its configuration if, and only if, the two widgets
      /// have [runtimeType] and [key] properties that are [operator==].
      ///
      /// If the widgets have no key (their key is null), then they are considered a
      /// match if they have the same type, even if their children are completely
      /// different.
      static bool canUpdate(Widget oldWidget, Widget newWidget) {
        return oldWidget.runtimeType == newWidget.runtimeType
            && oldWidget.key == newWidget.key;
      }
    }
    

    Widget 的 canUpdate 方法通过比较新部件和旧部件的 runtimeTypekey 属性是否相同来决定更新部件对应的 Element。

    Element

    Element 更像是一个Widget的实例化对象,Widget 调用createElement方法生成一对一关系的Element,Widget根据canUpdate方法来判断是否创建新的Element来替代旧的Element,这也是Flutter渲染效率高的原因所在,Element的复用。

    abstract class Element extends DiagnosticableTree implements BuildContext {
      /// Creates an element that uses the given widget as its configuration.
      ///
      /// Typically called by an override of [Widget.createElement].
      Element(Widget widget)
        : assert(widget != null),
          _widget = widget;
    /// The configuration for this element.
      @override
      Widget get widget => _widget!;
      Widget? _widget;
    @mustCallSuper
      void mount(Element? parent, Object? newSlot){...}
    @mustCallSuper
      void update(covariant Widget newWidget) {
        // This code is hot when hot reloading, so we try to
        // only call _AssertionError._evaluateAssertion once.
        assert(
          _lifecycleState == _ElementLifecycle.active
            && widget != null
            && newWidget != null
            && newWidget != widget
            && depth != null
            && Widget.canUpdate(widget, newWidget),
        );
        // This Element was told to update and we can now release all the global key
        // reservations of forgotten children. We cannot do this earlier because the
        // forgotten children still represent global key duplications if the element
        // never updates (the forgotten children are not removed from the tree
        // until the call to update happens)
        assert(() {
          _debugForgottenChildrenWithGlobalKey.forEach(_debugRemoveGlobalKeyReservation);
          _debugForgottenChildrenWithGlobalKey.clear();
          return true;
        }());
        _widget = newWidget;
      }
    

    RenderObject

    RenderObject 用于应用界面的布局和绘制,保存了元素的大小,布局等信息,实例化一个 RenderObject 是非常耗能的。

    abstract class RenderObjectWidget extends Widget {
      /// Abstract const constructor. This constructor enables subclasses to provide
      /// const constructors so that they can be used in const expressions.
      const RenderObjectWidget({ Key? key }) : super(key: key);
    
      /// RenderObjectWidgets always inflate to a [RenderObjectElement] subclass.
      @override
      @factory
      RenderObjectElement createElement();
    
      /// Creates an instance of the [RenderObject] class that this
      /// [RenderObjectWidget] represents, using the configuration described by this
      /// [RenderObjectWidget].
      ///
      /// This method should not do anything with the children of the render object.
      /// That should instead be handled by the method that overrides
      /// [RenderObjectElement.mount] in the object rendered by this object's
      /// [createElement] method. See, for example,
      /// [SingleChildRenderObjectElement.mount].
      @protected
      @factory
      RenderObject createRenderObject(BuildContext context);
    }
    

    如前文所说,只有继承自RenderObjectWidget的Widget对象并实现了createRenderObject方法创建一个RenderObject对象的Widget对象,才能成为Render Tree的一个组成部分。

    解析Widget、Element、Render 关系

    查看StatelessWidget源码:

    abstract class StatelessWidget extends Widget {
      /// Initializes [key] for subclasses.
      const StatelessWidget({ Key? key }) : super(key: key);
    
      /// Creates a [StatelessElement] to manage this widget's location in the tree.
      ///
      /// It is uncommon for subclasses to override this method.
      @override
      StatelessElement createElement() => StatelessElement(this);
    
      considerations.
      @protected
      Widget build(BuildContext context);
    }
    

    如前文所说,Widget 和 Element的一对一关系,StatelessWidget实现了createElement方法,将自身Widget作为参数,并返回了一个StatelessElement对象。
    对比StatefulWidget源码:

    abstract class StatefulWidget extends Widget {
      /// Initializes [key] for subclasses.
      const StatefulWidget({ Key? key }) : super(key: key);
    
      /// Creates a [StatefulElement] to manage this widget's location in the tree.
      ///
      /// It is uncommon for subclasses to override this method.
      @override
      StatefulElement createElement() => StatefulElement(this);
    
      @protected
      @factory
      State createState(); // ignore: no_logic_in_create_state, this is the original sin
    }
    

    我们可以分别在两个对象的createElement方法处打个断点,调试环境查看代码如何运行及第一个来的是哪个对象。

    第一个StatelessWidget
    第一个StatefullWidget

    断点调试,StatelessWidget和StatefullWidget 都会调用createElement方法,不同的是返回对象不同,一个是StatelessElement,一个是StatefulElement。但都是继承自ComponentElement,查看源码:

    class StatelessElement extends ComponentElement {
      /// Creates an element that uses the given widget as its configuration.
      StatelessElement(StatelessWidget widget) : super(widget);
    
      @override
      StatelessWidget get widget => super.widget as StatelessWidget;
    
      @override
      Widget build() => widget.build(this);
    
      @override
      void update(StatelessWidget newWidget) {
        super.update(newWidget);
        assert(widget == newWidget);
        _dirty = true;
        rebuild();
      }
    }
    class StatefulElement extends ComponentElement {
      /// Creates an element that uses the given widget as its configuration.
      StatefulElement(StatefulWidget widget)
          : _state = widget.createState(),
            super(widget) {
        assert(() {
          if (!state._debugTypesAreRight(widget)) {
            throw FlutterError.fromParts(<DiagnosticsNode>[
              ErrorSummary('StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>'),
              ErrorDescription(
                'The createState function for ${widget.runtimeType} returned a state '
                'of type ${state.runtimeType}, which is not a subtype of '
                'State<${widget.runtimeType}>, violating the contract for createState.',
              ),
            ]);
          }
          return true;
        }());
        assert(state._element == null);
        state._element = this;
        assert(
          state._widget == null,
          'The createState function for $widget returned an old or invalid state '
          'instance: ${state._widget}, which is not null, violating the contract '
          'for createState.',
        );
        state._widget = widget;
        assert(state._debugLifecycleState == _StateLifecycle.created);
      }
    
      @override
      Widget build() => state.build(this);
    
      /// The [State] instance associated with this location in the tree.
      ///
      /// There is a one-to-one relationship between [State] objects and the
      /// [StatefulElement] objects that hold them. The [State] objects are created
      /// by [StatefulElement] in [mount].
      State<StatefulWidget> get state => _state!;
      State<StatefulWidget>? _state;
    }
    

    继续调试模式,点击下一步⬇️,发现不管是StatelessWidget和StatefullWidget都会执行mount方法:


    mount方法调用时机

    我们来到ComponentElement的源码,看到mount方法:

    abstract class ComponentElement extends Element {
      /// Creates an element that uses the given widget as its configuration.
      ComponentElement(Widget widget) : super(widget);
    
      Element? _child;
    
      bool _debugDoingBuild = false;
      @override
      bool get debugDoingBuild => _debugDoingBuild;
    
      @override
      void mount(Element? parent, Object? newSlot) {
        super.mount(parent, newSlot);
        assert(_child == null);
        assert(_lifecycleState == _ElementLifecycle.active);
        _firstBuild();
        assert(_child != null);
      }
    
      void _firstBuild() {
        rebuild();
      }
    @override
      @pragma('vm:notify-debugger-on-exception')
      void performRebuild() {...}
    }
    

    按照mount -> _firstBuild -> rebuild -> performRebuild,option + command + b , 选择 ComponentElement ,看到built = build(); 最终来到StatelessElement 及 StatefulElement 中的build方法。然后对StatelessElement 和 StatefulElement 中的build 进行分析,发现遛了一大圈,终于到了build方法。

    结论

    1. StatelessElement 继承ComponentElement
      • 将自己(widget)传给父类ComponentElement
      • 调用build方法并将自己(Element)传回去,所以build(BuildContext context)方法里的context就是Element。
    2. StatefulElement 继承ComponentElement
      • 调用createState方法,创建state
      • 将element赋值给state,所以widget和state的element是同一个
      • 将widget赋值给state,所以在state里面可以通过widget点方法来获取到Widget中的数据
      • 调用state的build方法并将自己(Element)传出去

    RenderObjectWidget的创建

    自定义一个Row或者Column,查看源码,最终来到RenderObjectElement的源码:

    abstract class RenderObjectElement extends Element {
      /// Creates an element that uses the given widget as its configuration.
      RenderObjectElement(RenderObjectWidget widget) : super(widget);
    
      @override
      RenderObjectWidget get widget => super.widget as RenderObjectWidget;
    
      @override
      void mount(Element? parent, Object? newSlot) {
        super.mount(parent, newSlot);
        assert(() {
          _debugDoingBuild = true;
          return true;
        }());
        _renderObject = widget.createRenderObject(this);
        assert(!_renderObject!.debugDisposed!);
        assert(() {
          _debugDoingBuild = false;
          return true;
        }());
        assert(() {
          _debugUpdateRenderObjectOwner();
          return true;
        }());
        assert(_slot == newSlot);
        attachRenderObject(newSlot);
        _dirty = false;
      }
    }
    

    由此可见,继承自RenderObjectElement的Element最终会通过mount方法来调用createRenderObject方法,创建自己的render对象。
    结论
    RenderElement主要是创建RenderObject对象

    • 继承RenderObjectWidget的Widget会创建RenderElement
    • 创建RanderElement
    • Flutter会调用mount方法,调用createRanderObject方法

    相关文章

      网友评论

        本文标题:Flutter - 初探渲染原理

        本文链接:https://www.haomeiwen.com/subject/unmlqrtx.html