20分钟源码角度彻底了解Flutter的State生命周期以及Widget, Element, RenderObject的关系(超详细)
概述
Widget到底是什么?
Widget对于开发者而言,它可以是一个简单控件,也可以是个让程序员联想开发成App的一个页面。但是对于Flutter的渲染引擎来讲,却截然不同,它只是个配置,更贴切来讲,他是系统的视图模型ViewModel.他只是个数据载体,当系统需要渲染的时候,通过获取Widget对应的数据,来渲染成真正想要的视图。
Element像个什么?
不管是什么平台的App, 一个视图,它肯定是有创建,构建渲染,到结束的生命周期。那么,Flutter管理视图的生命周期的类是什么?对是他, 那个漆黑中最亮的仔–Element。我们在使用Widget的生命周期initState(),build(), disposed()等他的执行,是交给Element执行调用。因此,Element他的职能是一个项目经理,安排行程。专业的说,它充当控制器的角色,负责调配Widget生命周期。并且需要渲染的时候,把Widget携带的数据运输到渲染模块执行。
RenderObject才是幕后?
从上面已经了解到,Widget是他是个视图模型,而Element是Widget的管理者,那么Element把Widget的数据交给谁渲染呢?对,是这个在背后默默无闻,任劳任怨的耕作者—RenderObject。每一帧渲染 之前,Element会把Widget的数据交给到RenderObject,然后将其利用进行布局和绘制。
源码分析
Widget没有绘制职能
从Widget源码分析,就直接可以发现根本没有提供布局和绘制的工作.继承了DiagnosticableTree这个类,这个类主要作用是个标记性的接口,代表它是棵树的延伸,里面只是一些debug调试使用的信息之类的。
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key? key;
Element createElement();
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
StatelessWidget分析
下面是StatelessWidget的源码,只有2个方法, 创建StatelessElement,和构建build().但是从这里是无法知道他是怎么运转的。
abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key? key }) : super(key: key);
StatelessElement createElement() => StatelessElement(this);
Widget build(BuildContext context);
}
我们可以借助开始执行的第一行代码探索下去,看看有没有执行createElement的有关线索
void main(){
runApp(const MyApp());
}
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
class MyApp extends StatelessWidget {
//...
}
WidgetsFlutterBinding.ensureInitialized(),scheduleAttachRootWidget,scheduleWarmUpFrame这个三个方法到底做了那些东西可以去阅读下这篇文章点击>>30分钟彻底了解Flutter整个渲染流程(超详细)
,在这里不具体去进行展开描述。
我们去看看scheduleAttachRootWidget这个方法。
在运行App的时候,flutter会将MyApp绑定到RenderObjectToWidgetAdapter,构建成整颗Widget树。由于RenderObjectToWidgetAdapter他是个Widget,也就是Widget树的最顶层。
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
//..
}
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
//...
}
这里依旧没线索,我们深入attachToRenderTree这个方法看看。这个方法是完成渲染树的构建。由于是第一次执行,所以element一定是空的,所以他一定会执行createElement() ,
创建Element树的根结点,也就是RenderObjectToWidgetElement。
然后执行assignOwner,绑定唯一的BuildOwner。BuildOwner的buildScope()会对Element进行构建管理,触发RenderObject的布局和绘制工作等
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
owner.lockState(() {
//第一次调用会创建RenderObjectToWidgetElement
element = createElement();
assert(element != null);
//绑定BuildOwner,用来管理element状态
element!.assignOwner(owner);
});
//buildScope如果callback不为空,会执行callback回调
owner.buildScope(element!, () {
//挂载
element!.mount(null, null);
});
}
//...
return element!;
}
执行buildScope()时候,如果callback为空,首先会执行callback的回调。在这里也就会先执行element!.mount(null, null)。
void buildScope(Element context, [ VoidCallback? callback ]) {
if (callback != null) {
//..
callback();
}
}
Element的挂载mount()到底做了什么?
先看看RenderObjectToWidgetElement的mount做了什么。调用了父类RootRenderObjectElement的mount,然后执行重新构造_rebuild方法。先分析super.mount,再分析_rebuild.
下面我们可以看到,RenderObjectToWidgetElement是一个RenderObjectElement, 而RenderObjectElement本身也会调用super的Element的挂载方法 。
Element 的挂载
Element的挂载方法会做以下几件事情:
- 保存当前Element的爸爸是谁
- 保存当前的插槽位置信息,也就是当前Element在父Element索引位置,和对应的RenderObject
- 标记当前Element为活跃状态
- 保存当前的BuildOwner引用owner,由自顶向下传,保证所有的Element的BuildOwner始终是根节点Element的BuildOwner,从而保证全局唯一。
- 如果是key是GlobalKey,会被保存到owner,也就相当于将当前Element保存在全局。(通过GlobalKey你能在其他地方快速获取到当前state的数据,就是这个原因)
- 传递保存父Element的InheritedElement的集合(通过dependOnInheritedWidgetOfExactType方法,能获取到InheritedWidget的数据,就是这个原理,他保存到了这个集合。)
- 传递保存当前父Element的_NotificationNode(通过dispatchNotification能可以通知上面,就是这个原理, 它构成了一个通知树)
执行完Element的挂载,我们看看他的子类RenderObjectElement的挂载
RenderObjectElement 的挂载
RenderObjectElement的挂载方法会做以下几件事情:
- 创建渲染对象绑定到RenderObjectElement上,并且是1对1
- 插入绑定到父Element的RenderObject里面
- 并且更新更新提供Widget携带的数据
以上三方法,其实就是为了渲染之前做准备。
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
void mount(Element? parent, Object? newSlot) {
//...
super.mount(parent, newSlot);
_rebuild();
//...
}
}
abstract class RootRenderObjectElement extends RenderObjectElement {
//...
void mount(Element? parent, Object? newSlot) {
//...
super.mount(parent, newSlot);
}
//...
}
abstract class RenderObjectElement extends Element {
//...
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
//创建渲染对象绑定到RenderObjectElement上,1对1
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
//这个方法会绑定插槽,用来标记当前在Element什么位置,并且更新提供Widget携带的数据.
attachRenderObject(newSlot);
super.performRebuild();
}
void attachRenderObject(Object? newSlot) {
//绑定
_ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
final ParentDataElement<ParentData>? parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null) {
_updateParentData(parentDataElement.widget as ParentDataWidget<ParentData>);
}
}
//...
}
class MultiChildRenderObjectElement{
//...
void insertRenderObjectChild(RenderObject child, IndexedSlot<Element?> slot) {
final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> renderObject = this.renderObject;
renderObject.insert(child, after: slot.value?.renderObject);
}
//..
}
class BuildOwner{
//....
final Map<GlobalKey, Element> _globalKeyRegistry = <GlobalKey, Element>{};
void _registerGlobalKey(GlobalKey key, Element element) {
//。。。
_globalKeyRegistry[key] = element;
}
//...
}
///插槽信息
class IndexedSlot<T extends Element?> {
/// Information to define where the child occupying this slot fits in its
/// parent's child list.
final T value;
/// The index of this slot in the parent's child list.
final int index;
}
abstract class Element extends DiagnosticableTree implements BuildContext {
//参考下IndexedSlot
Object? get slot => _slot;
Object? _slot;
//...
void mount(Element? parent, Object? newSlot) {
//保存当前parent Element
_parent = parent;
//保存当前的插槽位置信息
_slot = newSlot;
//标记当前为活跃状态
_lifecycleState = _ElementLifecycle.active;
//计算当前深度
_depth = _parent != null ? _parent!.depth + 1 : 1;
//保存当前的owner
if (parent != null) {
// Only assign ownership if the parent is non-null. If parent is null
// (the root node), the owner should have already been assigned.
// See RootRenderObjectElement.assignOwner().
_owner = parent.owner;
}
//如果是key是GlobalKey,会被保存到owner,也就相当于将当前Element保存在全局
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
//传递保存当前父Element的InheritedElement集合
_updateInheritance();
//传递保存当前父Element的_NotificationNode
attachNotificationTree();
}
PersistentHashMap<Type, InheritedElement>? _inheritedWidgets;
_NotificationNode? _notificationTree;
//保存爸爸的通知
void attachNotificationTree() {
_notificationTree = _parent?._notificationTree;
}
//保存爸爸的_inheritedWidgets
void _updateInheritance() {
_inheritedWidgets = _parent?._inheritedWidgets;
}
//...
}
RenderObjectToWidgetElement 的挂载
上面讲到,RenderObjectToWidgetElement 执行super.mount()(上面已经分析完这里面流程)后,会执行_rebuild._rebuild接着他会执行父类Element的updateChild.
class RenderObjectToWidgetElement{
void _rebuild() {
try {
_child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot);
} catch (exception, stack) {
//...
_child = updateChild(null, error, _rootChildSlot);
}
}
}
、
Element的updateChild
这个方法会被执行很频繁,创建新的Element或者更新Element都是在这进行。
updateChild做了以下几件事,分3种情况:、
- 当前的Widget被置空,也就是移除了。那么就会进行调用deactivateChild移除Element工作,同时会把渲染对象移除,然后子视图就从屏幕消失了。(后面会详细分析移除的工作)
- 当前Element child是空的,也就是是第一次执行,当前的子视图还没创建,也就是会调用inflateWidget创建新的Element,并且挂载到Element树上。(如果key是GlobalKey,会复用owner上的的Element,然后做更新处理,并移除在树上的Element)
- 当前Element child不为空,也就是之前创建过子视图.
1). 如果当前的Widget的引用被保存了起来,也就是复用了,这时候不会创建新的Element或更新Element,只是对位置信息进行校验,如果不一样就更新。
2). 如果当前的Widget每次都是new的,并且Key不变情况下,会对更新Element的Widget, 执行update方法替换掉当前的widget.
并且如果当前的Element是StatefulElement,那么他会重写update,从而调用didUpdateWidget(oldWidget), 并且强制调用 State build方法。Element.update->Widget.didUpdateWidget->Element.build->State.build
3) 如果当前的Widget每次都是new的,并且Key的对象不一样,这种情况就会移除之前的Element, 执行inflateWidget创建新的Element,并且挂载到Element树上(如果key是GlobalKey,会复用owner上的的Element,然后做更新处理,并移除在树上的Element)。
综上所述,得出结论:
- 如果你想一个页面数据保持不变情况, 就复用Widget,不创建新的Widget,他就不会对Element更新或者创建,达到不重复渲染
- 如果你想一个页面被摧毁,并且重新走一次新的State,那么更改Key就完事了。
- 如果父StatefullWidget的State进行了setState,那么他的子StatefullWidget的State的didUpdateWidget一定会被执行
class Widget{
//...
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
//...
}
class StatefulElement extends ComponentElement{
//...
void update(StatefulWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
final StatefulWidget oldWidget = state._widget!;
state._widget = widget as StatefulWidget;
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
rebuild(force: true);
}
void rebuild({bool force = false}) {
//..
performRebuild();
//..
}
}
class ComponentElement{
//这个会执行StatelessWidget或StatefullWidget的build方法
void performRebuild() {
//...
built = build();
/...
}
}
class GloableKey{
//...
Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this];
//...
}
class Element{
void update(covariant Widget newWidget) {
//...
_widget = newWidget;
}
//...
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
final Element newChild;
//如果当前的Widget为空了,那么就进行移除工作。
if (newWidget == null) {
if (child != null) {
//将当前的子Element移除
deactivateChild(child);
}
return null;
}
//如果有创建过子视图
if (child != null) {
//如果当前的newWidget对象不变,也就是被复用了widget.这时候只是对插槽信息进行校验更新
if (hasSameSuperclass && child.widget == newWidget) {
//如果插槽变化了,就更新
if (child.slot != newSlot) {
updateSlotForChild(child, newSlot);
}
newChild = child;
}
//如果是同一个Widget一样情况下并且key一样情况下,
else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
//如果插槽变化了,就更新
if (child.slot != newSlot) {
updateSlotForChild(child, newSlot);
}
//
child.update(newWidget);
newChild = child;
} else {
//移除之前的Element
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
}
}
//如果没创建过子视图,去创建子视图
else {
newChild = inflateWidget(newWidget, newSlot);
}
//...
return newChild;
}
//获取buildOwner上已缓存的Element
Element? _retakeInactiveElement(GlobalKey key, Widget newWidget) {
final Element? element = key._currentElement;
return element;
}
Element inflateWidget(Widget newWidget, Object? newSlot) {
//如果当前key是GlobalKey
//如果内存中存在,那么复用然后更新返回
if (key is GlobalKey) {
final Element? newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild!;
}
}
//...
//创建新的Element
final Element newChild = newWidget.createElement();
//挂载到树上
newChild.mount(this, newSlot);
//...
}
}
RenderObjectToWidgetElement当前是第一次执行,所以他的子Element是空的,也就是会创建第一个新的Element到里面去。当前的入参Widget肯定是MyApp,在这终于关联起来了。newWidget.createElement(),也就是MyApp().createElement(), 获取到StatelessElement,然后挂载到上面去。
class MyApp extends StatelessWidget {
Widget build(BuildContext context){
//...
}
}
abstract class StatelessWidget extends Widget {
///...
StatelessElement createElement() => StatelessElement(this);
}
ComponentElement的挂载会执行Widget的build方法
由于StatelessElement是ComponentElement, ComponentElement的挂载会执行构建工作,因此MyApp在这时候,一定会被执行build()方法。
class ComponentElement extends Element {
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
//...
_firstBuild();
}
void _firstBuild() {
// StatefulElement overrides this to also call state.didChangeDependencies.
rebuild(); // This eventually calls performRebuild.
}
void rebuild({bool force = false}) {
//...
performRebuild();
//...
}
void performRebuild() {
//...
built = build();
//..
}
}
从上面已经得知,StatelessWidget只能被动挂载从而触发build()方法,那么想要主动刷新怎么办?没错,你已经猜对了,为了解决这个问题,因此Flutter加了StatefullWidget这个Widget.
StatefullWidget
从上面得知, 当Element的子Element为空的时候,就会调用当前子Widget的createElement()
那么在这里,StatefulWidget创建的Element是StatefulElement
abstract class StatefulWidget extends Widget {
StatefulElement createElement() => StatefulElement(this);
State createState();
}
StatefulElement的创建
class StatefulElement extends ComponentElement {
State<StatefulWidget>? _state;
///...
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
///...
state._element = this;
state._widget = widget;
///...
}
}
显而易见,在创建StatefulElement,做了以下4件事情:
- 执行State的createState
- 调用super构造, 持有当前Widget
- 让State和StatefulElement相互引用
- 让State同时持有当前的Widget
StatefulElement的挂载
从上面得知,ComponentElement挂载的时候会执行_firstBuild进行第一次构建,而StatefulElement是ComponentElement的子类,并且重写了_firstBuild方法
abstract class ComponentElement extends Element {
/// Creates an element that uses the given widget as its configuration.
ComponentElement(super.widget);
Element? _child;
bool _debugDoingBuild = false;
bool get debugDoingBuild => _debugDoingBuild;
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
//..
_firstBuild();
}
void _firstBuild() {
//...
rebuild();
//...
}
void rebuild(){
//...
performRebuild();
//..
}
void performRebuild(){
built = build();
//...更新Element
_child = updateChild(_child, built, slot);
}
}
class StatefulElement extends ComponentElement {
void _firstBuild() {
//..
final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
//...
state.didChangeDependencies();
//调用 build() 方法,从而调用state的build方法
super._firstBuild();
}
Widget build() => state.build(this);
}
StatefullWidget的State生命周期
State的初始化流程
从上面得知,_firstBuild会执行如下几件事情
- 执行当前State的initState()
- 执行当前的didChangeDependencies
- 执行ComponentElement的_firstBuild,从而执行performRebuild,然后执行StatefulElement的build(),也就是state.build(this)
State的更新流程
上面初始化后,会完成一帧的绘制。如果想第二次刷新,总所周知,调用setState方法就好了。接下来分析setState做了什么。
class State<T extends StatefulWidget> with Diagnosticable {
//...
void setState(VoidCallback fn) {
//....
final Object? result = fn() as dynamic;
//..
//....
_element!.markNeedsBuild();
}
}
setState首选会执行callback里面内容,然后执行markNeedsBuild这个方法.
markNeedsBuild做了如下2件事情:
- 将当前Element的_dirty改为true,代表当前的Element需要进行更新页面操作
- 调用owner的scheduleBuildFor方法,scheduleBuildFor会将当前的Element放到所有Element需要待更新的列表里面,等到下一次Vsyn,(不熟悉,点击>>Vsync)到来的时候,在BuildOwner里的_dirtyElements这个列表里面Element会被执行更新操作
这样的设计,实质就是经典的消费者与生产者模型。
abstract class Element extends DiagnosticableTree implements BuildContext{
//...
void markNeedsBuild() {
//...
_dirty = true;
owner!.scheduleBuildFor(this);
}
}
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
void initInstances() {
super.initInstances();
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
//...
}
void _handleBuildScheduled() {
//..
ensureVisualUpdate();
//...
}
}
class BuildOwner{
//所有待更新的Element都放在这,然后调用onBuildScheduled执行Vsync申请,下一次Vsync来的时候,这些Element都会被执行更新操作
final List<Element> _dirtyElements = <Element>[];
void scheduleBuildFor(Element element) {
//...
//这个方法的是在WidgetsBinding初始化设置的,onBuildScheduled会执行BuildOwner的_handleBuildScheduled会申请Vsync
onBuildScheduled!();
//...
//添加到待更新的Element列表
_dirtyElements.add(element);
element._inDirtyList = true;
//...
}
}
每一次Vsync到来,都会回调WidgetsBinding的drawFrame方法(详细的Flutter渲染流程可以看看我的这篇文章<<30分钟彻底了解Flutter整个渲染流程>>), 在这篇文章我们知道了, 所有Element的buildOwner都是由WidgetsBinding的buildOwner传递过去的,所以全局唯一。也就是说, 所有需要更新的Element加到_dirtyElements这个列表里面,实质就是加到WidgetsBinding的BuildOwner里面。
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
//..
void drawFrame() {
buildOwner!.buildScope(renderViewElement!);
super.drawFrame();
}
}
class BuildOwner {
//..
void buildScope(Element context, [ VoidCallback? callback ]) {
//...
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
//...
final Element element = _dirtyElements[index];
assert(element != null);
assert(element._inDirtyList);
//...
element.rebuild();
}
//
}
}
class Element{
//...
void rebuild({bool force = false}) {
performRebuild();
}
}
class ComponentElement{
void performRebuild() {
//...
built = build();
/...
}
}
class StatefulElement extends ComponentElement{
Widget build() => state.build(this);
}
State的didUpdateWidget
从上面得知,如果当前的Widget每次都是new的,并且Key不变情况下,会对更新Element的Widget, 执行update方法替换掉当前的widget.然而当前的Element是StatefulElement,那么他会重写update,从而调用didUpdateWidget(oldWidget), 并且强制调用 State build方法。Element.update->Widget.didUpdateWidget->Element.build->State.build
void update(StatefulWidget newWidget) {
//...
final StatefulWidget oldWidget = state._widget!;
state._widget = widget as StatefulWidget;
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
//...
rebuild(force: true);
}
State的activate()
让我们再回到inflateWidget这个方法。
在inflateWidget中, 如果key是GlobalKey情况,会做如下几件事:
- 获取BuildOwner上的Element进行复用,包括RenderObject
- 回调Element的activate()方法,并遍历子element都进行activate,
- 将同类型已存在树上的Element移除
- 获取到BuildOwner的Element后,进行复用RenderObject到上面
以上个方法其实就是挂载的 逻辑,但是element和renderObject是复用的
class GloableKey{
//...
Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this];
//...
}
class BuildOwner{
//....
final Map<GlobalKey, Element> _globalKeyRegistry = <GlobalKey, Element>{};
void _registerGlobalKey(GlobalKey key, Element element) {
//。。。
_globalKeyRegistry[key] = element;
}
//...
}
class Element{
Element inflateWidget(Widget newWidget, Object? newSlot) {
//如果当前key是GlobalKey
if (key is GlobalKey) {
//如果内存中存在,那么复用然后更新返回,并在树中移除掉
final Element? newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
//在这里会调用Element的activate方法,并且会将之前的渲染对象绑定进来
newChild._activateWithParent(this, newSlot);
final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild!;
}
}
//...
//创建新的Element
final Element newChild = newWidget.createElement();
//挂载到树上
newChild.mount(this, newSlot);
//...
}
Element? _retakeInactiveElement(GlobalKey key, Widget newWidget) {
//获取BuildOwner上的ELement返回
final Element? element = key._currentElement;
//下面会将element在树中移除
final Element? parent = element._parent;
if (parent != null) {
parent.forgetChild(element);
parent.deactivateChild(element);
}
assert(element._parent == null);
owner!._inactiveElements.remove(element);
return element;
}
//复用性挂载
void _activateWithParent(Element parent, Object? newSlot) {
_parent = parent;
//...
_activateRecursively(this);
attachRenderObject(newSlot);
}
static void _activateRecursively(Element element) {
//调用activate
element.activate();
//调用所有子Element进行activate
element.visitChildren(_activateRecursively);
}
void activate() {
//..
//传递_inheritedWidgets,上面讲过了这个方法
_updateInheritance();
//传递Notification,上面讲过了这个方法
attachNotificationTree();
if (_dirty) {
//将element加到待更新队列
owner!.scheduleBuildFor(this);
}
//...
}
}
State的摧毁流程
让我们再次回到ComponentElement的updateChild方法.上面得知,在Widget为空的时候,就会进行移除这个和他绑定关系的。
或者当前的Widget被替换掉成其他的Widget也会进行移除Element操作。
比如:
Widget build(BuildContext context){
return Widget1();
}
Widget build(BuildContext context){
return Widget2();
}
Widget1将会被移,然后被Widget2取代。
接下来我们再次看看updateChild
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
//如果不要这个Widget了,就会把这个Widget对应的Element移除
if (newWidget == null) {
if (child != null) {
deactivateChild(child);
}
return null;
}
if (hasSameSuperclass && child.widget == newWidget) {
//...
}else{
//移除原来的Elenent
deactivateChild(child);
//填充新的Widget
newChild = inflateWidget(newWidget, newSlot);
}
//..
}
State的deactivate
deactivateChild会处理以下几件事:
- 切断父Element
- 调用detachRenderObject移除_slot,从上面得知,这个_slot,会保存索引信息(index),和这个Widget对应的Element(IndexedSlot的Value)。由于Element会持有RenderObject, 所以detachRenderObject顾名思义就是移除RenderObject
- 执行owner!._inactiveElements.add(child), 这个方法会将element放到下一Vsync来后进行接触挂载工作(后面会分析到),并且将会执行_deactivateRecursively,然后让element执行deactivate,并且他的所有子element也会被调用执行deactivate。Element的deactivate会将状态标记成不活跃状态,并且把_inheritedWidgets置空,子Element将不再能通过dependOnInheritedWidgetOfExactType获取到上面Element的数据
class IndexedSlot<T extends Element?> {
/// Information to define where the child occupying this slot fits in its
/// parent's child list.
final T value;
/// The index of this slot in the parent's child list.
final int index;
}
class _InactiveElements {
//这里的Element将会在下一个Vsync来时候被解除挂载
final Set<Element> _elements = HashSet<Element>();
void add(Element element) {
if (element._lifecycleState == _ElementLifecycle.active) {
_deactivateRecursively(element);
}
_elements.add(element);
}
static void _deactivateRecursively(Element element) {
//...
element.deactivate();
//...
element.visitChildren(_deactivateRecursively);
//...
}
class Element{
void deactivateChild(Element child) {
//...
child._parent = null;
//移除_slot, 从而移除Element和RenderObject
child.detachRenderObject();
owner!._inactiveElements.add(child); // this eventually calls child.deactivate()
//..
}
void detachRenderObject() {
visitChildren((Element child) {
child.detachRenderObject();
});
_slot = null;
}
void deactivate() {
//....
_inheritedWidgets = null;
_lifecycleState = _ElementLifecycle.inactive;
}
}
而StatefulElement重写了deactivate() ,被调用会被执行state.deactivate. 因此,State的 deactivate是在被Element被移除的时候被调用的。
State的dispose()
我们知道当下一个Vsync来的时候,WidgetsBinding的drawFrame会被执行,不了解这个流程的看我的这个篇文章<<30分钟彻底了解Flutter整个渲染流程>>
这个方法里面不仅仅有构建和渲染的工作,还有一个活就是解除挂载,执行摧毁Element的逻辑。是在buildOwner的finalizeTree这个方法.
class WidgetsBinding{
void drawFrame() {
//...
if (renderViewElement != null) {
buildOwner!.buildScope(renderViewElement!);
}
super.drawFrame();
buildOwner!.finalizeTree();
//...
}
finalizeTree这方法对之前在deactivate时候, 被添加到BuildOwner的_InactiveElements的待解除挂载的_elements所有Element进行unmount操作,
class BuildOwner{
//...
void finalizeTree() {
//...
lockState(_inactiveElements._unmountAll);
//...
}
//...
}
class _InactiveElements{
final Set<Element> _elements = HashSet<Element>();
//解除挂载所有的_elements里面的Element
void _unmountAll() {
//...
elements.reversed.forEach(_unmount);
//...
}
//先摧毁所有子ELement,最后摧毁当前ELement
void _unmount(Element element) {
//..
element.visitChildren((Element child) {
_unmount(child);
});
element.unmount();
//..
}
}
Element的unmount
在Element的unmount,如果当前Key是GlobalKey,那么这个Element将会从全局中移除掉,然后将_widget去掉
class Element{
void unmount() {
final Key? key = _widget?.key;
if (key is GlobalKey) {
//将Element从全局中移除掉
owner!._unregisterGlobalKey(key, this);
}
//_widget移除置空
_widget = null;
///...
}
}
RenderObjectElement的unmount
RenderObjectElement执行父类Element的unmount业务后,进行renderObject移除数据等操作,比如RawImage,
abstract class RenderObjectElement extends Element {
void unmount() {
//...
final RenderObjectWidget oldWidget = widget as RenderObjectWidget;
super.unmount();
//...
//通知widget把渲染对象相关的数据进行移除
oldWidget.didUnmountRenderObject(renderObject);
//将当前渲染图层移除,执行RenderObject的回收工作的生命周期方法dispose
_renderObject!.dispose();
_renderObject = null;
}
}
class RawImage extends LeafRenderObjectWidget {
//...
void didUnmountRenderObject(RenderImage renderObject) {
// Have the render object dispose its image handle.
renderObject.image = null;
}
//...
}
StatefulElement的unmount
由于StatefulElement是ComponentElement,并没有渲染对象,所以只需要进行父类Element的业务,然后将
State的dispose()执行,将_element和_state置空就好
class StatefulElement extends ComponentElement {
void unmount() {
super.unmount();
//..
state.dispose();
//..
state._element = null;
// Release resources to reduce the severity of memory leaks caused by
// defunct, but accidentally retained Elements.
_state = null;
}
}
总结下,一个Element被移除的流程。
- 当Widget被替换或被置空的时候,对应的Element就会被添加到BuildOwner的_InactiveElements的列表里面,此刻State的deactivate是在这里被回调
- 当下个Vsync来的时候,WidgetsFlutterBinding的drawFrame就会将BuildOwner的_InactiveElements的列表所有的Element进行unmount操作
- StatefulElement在unmount时候会对State调用dispose()完成最后的生命周期工作
整个流程就到这里了,如果这篇文章对你有帮助,请关注🙏,点赞👍,收藏😋三连哦