【Flutter脱发实录】盘一盘Element

1,453 阅读6分钟

前面的两篇小记,介绍了WidgetRenderObjectWidget代表了开发人员的想法,RenderObject负责在屏幕上进行绘制。那么想法如何驱动RenderObject工作呢?那么就需要引入Element这个中介,今天就来盘它一盘!

官方解释

An instantiation of a [Widget] at a particular location in the tree.

** Widget在UI树某个具体位置的实例**

阅读源码

要知道Element是什么?干了什么?必然逃不过看源码!

abstract class Element extends DiagnosticableTree implements BuildContext {
  // 构造器传入Widget 并持有它
  Element(Widget widget)
      : assert(widget != null),
        _widget = widget;

  Element _parent;

  // 当前element在父element的children中的位置
  // 当element只有一个child时 应该置null
  dynamic _slot;

  // 当前element在element tree 上的层级
  // root element = 0
  // child.depth=parent.depth+1
  int _depth;

  // 持有的widget对象
  Widget _widget;

  // element生命周期的管理类
  BuildOwner _owner;

  bool _active = false;

  // 遍历标记重建 仅debug模式下 hot reload
  void reassemble() {
    markNeedsBuild();
    visitChildren((Element child) {
      child.reassemble();
    });
  }

  // 如果当前为 RenderObjectElement 则返回当前节点对应的render object
  // 否则从当前节点向下寻找,返回第一个render object
  RenderObject get renderObject {
    RenderObject result;
    void visit(Element element) {
      if (element is RenderObjectElement)
        result = element.renderObject;
      else
        element.visitChildren(visit);
    }

    visit(this);
    return result;
  }

  // 根据widget配置的变化,更新child
  // 这个方法是整个widget体系的核心方法
  @protected
  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    if (newWidget == null) {
      if (child != null) deactivateChild(child);
      return null;
    }
    Element newChild;
    if (child != null) {
      bool hasSameSuperclass = true;
      if (hasSameSuperclass && child.widget == newWidget) {
        if (child.slot != newSlot) updateSlotForChild(child, newSlot);
        newChild = child;
      } else if (hasSameSuperclass &&
          Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot) updateSlotForChild(child, newSlot);
        child.update(newWidget);
        newChild = child;
      } else {
        deactivateChild(child);
        newChild = inflateWidget(newWidget, newSlot);
      }
    } else {
      newChild = inflateWidget(newWidget, newSlot);
    }
    return newChild;
  }

  // 将element挂载到element tree的指定位置
  // 初始化操作
  // initial -> active
  @mustCallSuper
  void mount(Element parent, dynamic newSlot) {
    _parent = parent;
    _slot = newSlot;
    _depth = _parent != null ? _parent.depth + 1 : 1;
    _active = true;
    if (parent != null) // Only assign ownership if the parent is non-null
      _owner = parent.owner;
    if (widget.key is GlobalKey) {
      final GlobalKey key = widget.key;
      key._register(this);
    }
    _updateInheritance();
  }

  // 更新持有的widget配置
  @mustCallSuper
  void update(covariant Widget newWidget) {
    _widget = newWidget;
  }

  // 遍历更新节点在render tree的位置
  @protected
  void updateSlotForChild(Element child, dynamic newSlot) {
    void visit(Element element) {
      element._updateSlot(newSlot);
      if (element is! RenderObjectElement) element.visitChildren(visit);
    }

    visit(child);
  }

  // 更新当前节点在render tree上的位置
  void _updateSlot(dynamic newSlot) {
    _slot = newSlot;
  }

  // 遍历更新节点深度
  void _updateDepth(int parentDepth) {
    final int expectedDepth = parentDepth + 1;
    if (_depth < expectedDepth) {
      _depth = expectedDepth;
      visitChildren((Element child) {
        child._updateDepth(expectedDepth);
      });
    }
  }

  // 从自己和自己的子节点render tree 上移除  slot = null
  void detachRenderObject() {
    visitChildren((Element child) {
      child.detachRenderObject();
    });
    _slot = null;
  }

  // 在指定位置插入render tree
  void attachRenderObject(dynamic newSlot) {
    visitChildren((Element child) {
      child.attachRenderObject(newSlot);
    });
    _slot = newSlot;
  }

  // 从不活跃element列表中,取出并重新激活,复用element降低开销
  // Widget.canUpdate(oldWidget, newWidget)判断两次配置是否可以在原基础上升级
  Element _retakeInactiveElement(GlobalKey key, Widget newWidget) {
    final Element element = key._currentElement;
    if (element == null) return null;
    if (!Widget.canUpdate(element.widget, newWidget)) return null;
    final Element parent = element._parent;
    if (parent != null) {
      parent.forgetChild(element);
      parent.deactivateChild(element);
    }
    owner._inactiveElements.remove(element);
    return element;
  }

  // 填充widget  根据新的widget配置在指定位置生成一个element 并mount到tree
  // 如果有可复用的element 则在原element上根据newWidget更新
  @protected
  Element inflateWidget(Widget newWidget, dynamic newSlot) {
    final Key key = newWidget.key;
    if (key is GlobalKey) {
      final Element newChild = _retakeInactiveElement(key, newWidget);
      if (newChild != null) {
        newChild._activateWithParent(this, newSlot);
        final Element updatedChild = updateChild(newChild, newWidget, newSlot);
        return updatedChild;
      }
    }
    final Element newChild = newWidget.createElement();
    newChild.mount(this, newSlot);
    return newChild;
  }

  // 将一个element从tree移除,并置为不活跃状态
  @protected
  void deactivateChild(Element child) {
    child._parent = null;
    child.detachRenderObject();
    owner._inactiveElements
        .add(child); // this eventually calls child.deactivate()
  }

  // 激活
  void _activateWithParent(Element parent, dynamic newSlot) {
    _parent = parent;
    _updateDepth(_parent.depth);
    _activateRecursively(this);
    attachRenderObject(newSlot);
  }

  static void _activateRecursively(Element element) {
    element.activate();
    element.visitChildren(_activateRecursively);
  }

  // inactive -> active
  // 只有再次激活时才会调用  初次创建不调用
  @mustCallSuper
  void activate() {
    final bool hadDependencies =
        (_dependencies != null && _dependencies.isNotEmpty) ||
            _hadUnsatisfiedDependencies;
    _active = true;
    _dependencies?.clear();
    _hadUnsatisfiedDependencies = false;
    _updateInheritance();
    if (_dirty) owner.scheduleBuildFor(this);
    if (hadDependencies) didChangeDependencies();
  }

  // 冻结 active -> inactive
  @mustCallSuper
  void deactivate() {
    if (_dependencies != null && _dependencies.isNotEmpty) {
      for (InheritedElement dependency in _dependencies)
        dependency._dependents.remove(this);
    }
    _inheritedWidgets = null;
    _active = false;
  }

  // 卸载  无法再被激活 inactive -> defunct
  @mustCallSuper
  void unmount() {
    if (widget.key is GlobalKey) {
      final GlobalKey key = widget.key;
      key._unregister(this);
    }
  }

  // 获取持有的render object对象
  @override
  RenderObject findRenderObject() => renderObject;

  // 获取对应render object的size
  @override
  Size get size {
    final RenderObject renderObject = findRenderObject();
    if (renderObject is RenderBox) return renderObject.size;
    return null;
  }

  // 当前有依赖关系的widget对应的element map存储
  Map<Type, InheritedElement> _inheritedWidgets;
  // 与当前element有依赖的祖先 set类型
  Set<InheritedElement> _dependencies;
  bool _hadUnsatisfiedDependencies = false;

  // 获取某个祖先InheritedElement持有的InheritedWidget  并且将此InheritedElement注册为依赖的祖先
  @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor,
      {Object aspect}) {
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }

  // 获取祖先节点Type为T的Widget对象  InheritedWidget子类能访问父类的核心原理
  @override
  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>(
      {Object aspect}) {
    final InheritedElement ancestor =
        _inheritedWidgets == null ? null : _inheritedWidgets[T];
    if (ancestor != null) {
      return dependOnInheritedElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

  @override
  InheritedElement
      getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
    final InheritedElement ancestor =
        _inheritedWidgets == null ? null : _inheritedWidgets[T];
    return ancestor;
  }

  void _updateInheritance() {
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

  // 一直向上寻找Widget类型为T的,并且返回第一个
  @override
  T findAncestorWidgetOfExactType<T extends Widget>() {
    Element ancestor = _parent;
    while (ancestor != null && ancestor.widget.runtimeType != T)
      ancestor = ancestor._parent;
    return ancestor?.widget;
  }

  // 向上查找第一个类型为T的State
  @override
  T findAncestorStateOfType<T extends State<StatefulWidget>>() {
    Element ancestor = _parent;
    while (ancestor != null) {
      if (ancestor is StatefulElement && ancestor.state is T) break;
      ancestor = ancestor._parent;
    }
    final StatefulElement statefulAncestor = ancestor;
    return statefulAncestor?.state;
  }

  // 向上查找最后一个类型为T的State
  @override
  T findRootAncestorStateOfType<T extends State<StatefulWidget>>() {
    Element ancestor = _parent;
    StatefulElement statefulAncestor;
    while (ancestor != null) {
      if (ancestor is StatefulElement && ancestor.state is T)
        statefulAncestor = ancestor;
      ancestor = ancestor._parent;
    }
    return statefulAncestor?.state;
  }

  // 向上查找第一个类型为T的RenderObject
  @override
  T findAncestorRenderObjectOfType<T extends RenderObject>() {
    Element ancestor = _parent;
    while (ancestor != null) {
      if (ancestor is RenderObjectElement && ancestor.renderObject is T) break;
      ancestor = ancestor._parent;
    }
    final RenderObjectElement renderObjectAncestor = ancestor;
    return renderObjectAncestor?.renderObject;
  }

  @override
  void visitAncestorElements(bool visitor(Element element)) {
    Element ancestor = _parent;
    while (ancestor != null && visitor(ancestor)) ancestor = ancestor._parent;
  }

  // 当依赖发生改变时 需要重建
  @mustCallSuper
  void didChangeDependencies() {
    markNeedsBuild();
  }

  bool get dirty => _dirty;
  bool _dirty = true;

  bool _inDirtyList = false;

  void markNeedsBuild() {
    if (!_active) return;
    if (dirty) return;
    _dirty = true;
    owner.scheduleBuildFor(this);
  }

  void rebuild() {
    if (!_active || !_dirty) return;
    performRebuild();
  }

  @protected
  void performRebuild();
}

生命周期

作为一个实体,那么Element就有它的生命周期:

enum _ElementLifecycle {
  initial, // 初始化
  active, // 活跃
  inactive, // 不活跃
  defunct, // 废弃
}

Element各方法与生命周期的关系:

Widget.createElement() => initial
mount() => initial -> active
update() => 只有在active状态时才会生效
deactivate() => active -> inactive
activate() => inactive -> active
unmount() => inactive -> defunct

ElementWidget的关系

Element 的创建

从构造器可以看到,传入了一个Widget对象,并持有这个对象。那么这个构造器何时被调用,即Element何时被创建呢?

Widget篇中提到,Widget有一个createElement()方法标记为@protected,看看它的几个关键的子类是如何实现的:

StatelessWidget => StatelessElement createElement() => StatelessElement(this);
StatefulWidget => StatefulElement createElement() => StatefulElement(this);
SingleChildRenderObjectWidget => SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
MultiChildRenderObjectWidget => MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);
InheritedWidget => InheritedElement createElement() => InheritedElement(this);
ParentDataWidget<T extends ParentData> => ParentDataElement<T> createElement() => ParentDataElement<T>(this);

可以看到,每个Widget都有其专有的Element的,并且在createElement()方法中,创建它们。从Elementslotdepth字段可以得到,Element就是Widget在整个UI树中的一个具体实例。

Element 的更新

UI树已经被构建,当某个节点需要变化时,如何优雅地更新这棵UI树,是整个Flutter性能表现的重中之重。

updateChild方法中,体现的思想就是,比对配置,能复用就复用。

  // 根据widget配置的变化,更新child
  // 这个方法是整个widget体系的核心方法
  @protected
  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    // 更新后的widget为null,即widget被移除 则将其对应的element 冻结
    if (newWidget == null) {
      if (child != null) deactivateChild(child);
      return null;
    }
    Element newChild;
    // 更新后的widget不为null
    if (child != null) {
      // child不为null 即该节点存在
      bool hasSameSuperclass = true; // debug字段
      if (hasSameSuperclass && child.widget == newWidget) {
        // 更新后的widget与原来的widget完全相同  则只更新slot在UI树中的位置
        if (child.slot != newSlot) updateSlotForChild(child, newSlot);
        newChild = child;
      } else if (hasSameSuperclass &&
          Widget.canUpdate(child.widget, newWidget)) {
        // 新老不完全相同 则判断是否可以在原element上更新 
        // oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key
        // 如果需要更新位置 则更新位置
        if (child.slot != newSlot) updateSlotForChild(child, newSlot);
        // 更换持有的widget对象为newWidget
        child.update(newWidget);
        newChild = child;
      } else {
        // newWidget无法在原来的widget基础上更新 则冻结原来的element,新建一个element插入tree的slot位置
        deactivateChild(child);
        newChild = inflateWidget(newWidget, newSlot);
      }
    } else {
      // child为null  即新增的widget 直接创建
      newChild = inflateWidget(newWidget, newSlot);
    }
    return newChild;
  }

BuildContext

源码的第一行可以看到,Element实现了BuildContext这个抽象接口类。并且可以发现,Element中的大部分方法都是对BuildContext的实现。

当我们用StatelessWidgetStatefulWidget搭积木时,也会发现build方法都会传一个BuildContext对象:

Widget build(BuildContext context) {}

用这个context,我们可以:

  • 获取主题Theme.of(context)
  • 获取屏幕信息MediaQuery.of(context)
  • 路由Navigator.of(context)
  • 获取大小context.size

查看Theme源码,Theme.of(context)方法实际是通过dependOnInheritedWidgetOfExactType方法,全局找到_InheritedTheme类,获取其theme属性,然后根据didChangeDependencies机制,触发子节点的重绘,达到主题的效果。

这些方法是不是很熟悉?是的,都是Element的职责范畴,既然它的职权这么广,作用那么大,必然需要保护起来,屏蔽对其的直接访问。从而引入了BuildContext,间接对其访问。

ElementRenderObject的关系

RenderObject 的创建

之前说过,Widget虽然指定了RenderObject,但并没有真正创建。那么RenderObject是什么时候被创建的呢?看下RenderObjectElement的源码,找到mount方法:

abstract class RenderObjectElement extends Element {
  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    // 此时才真正的创建,并持有renderobject
    _renderObject = widget.createRenderObject(this);
    // 插入到render tree
    attachRenderObject(newSlot);
    _dirty = false;
  }
}

ElementRenderObject的操作

具体可见RenderObjectElement,本文就暂不详细说明了。

总结

经过Element的串连,整个Flutter的UI机制的思路也变得清晰起来。Element就像是一位产品经理,将客户Widget提出的构想和需求变更,整理之后,安排各岗位的小伙伴RenderObject,按照开发流程,一步一步开发,bugfix,最终完成客户的期望。

深夜了,给自己来碗黑芝麻糊恰恰吧!