前面的两篇小记,介绍了Widget
和RenderObject
,Widget
代表了开发人员的想法,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
Element
和Widget
的关系
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()
方法中,创建它们。从Element
的slot
和depth
字段可以得到,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
的实现。
当我们用StatelessWidget
和StatefulWidget
搭积木时,也会发现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
,间接对其访问。
Element
和RenderObject
的关系
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;
}
}
Element
对RenderObject
的操作
具体可见RenderObjectElement
,本文就暂不详细说明了。
总结
经过Element
的串连,整个Flutter的UI机制的思路也变得清晰起来。Element
就像是一位产品经理,将客户Widget
提出的构想和需求变更,整理之后,安排各岗位的小伙伴RenderObject
,按照开发流程,一步一步开发,bugfix,最终完成客户的期望。
深夜了,给自己来碗黑芝麻糊恰恰吧!