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,绑定唯一的BuildOwnerBuildOwnerbuildScope()会对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()到底做了什么?

先看看RenderObjectToWidgetElementmount做了什么。调用了父类RootRenderObjectElement的mount,然后执行重新构造_rebuild方法。先分析super.mount,再分析_rebuild.
下面我们可以看到,RenderObjectToWidgetElement是一个RenderObjectElement, 而RenderObjectElement本身也会调用super的Element的挂载方法 。

Element 的挂载

Element的挂载方法会做以下几件事情:

  1. 保存当前Element的爸爸是谁
  2. 保存当前的插槽位置信息,也就是当前Element在父Element索引位置,和对应的RenderObject
  3. 标记当前Element为活跃状态
  4. 保存当前的BuildOwner引用owner,由自顶向下传,保证所有的ElementBuildOwner始终是根节点Element的BuildOwner,从而保证全局唯一。
  5. 如果是key是GlobalKey,会被保存到owner,也就相当于将当前Element保存在全局。(通过GlobalKey你能在其他地方快速获取到当前state的数据,就是这个原因)
  6. 传递保存父Element的InheritedElement的集合(通过dependOnInheritedWidgetOfExactType方法,能获取到InheritedWidget的数据,就是这个原理,他保存到了这个集合。)
  7. 传递保存当前父Element的_NotificationNode(通过dispatchNotification能可以通知上面,就是这个原理, 它构成了一个通知树)

执行完Element的挂载,我们看看他的子类RenderObjectElement的挂载

RenderObjectElement 的挂载

RenderObjectElement的挂载方法会做以下几件事情:

  1. 创建渲染对象绑定到RenderObjectElement上,并且是1对1
  2. 插入绑定到父Element的RenderObject里面
  3. 并且更新更新提供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种情况:、

  1. 当前的Widget被置空,也就是移除了。那么就会进行调用deactivateChild移除Element工作,同时会把渲染对象移除,然后子视图就从屏幕消失了。(后面会详细分析移除的工作)
  2. 当前Element child是空的,也就是是第一次执行,当前的子视图还没创建,也就是会调用inflateWidget创建新的Element,并且挂载到Element树上。(如果key是GlobalKey,会复用owner上的的Element,然后做更新处理,并移除在树上的Element)
  3. 当前Element child不为空,也就是之前创建过子视图.
    1). 如果当前的Widget的引用被保存了起来,也就是复用了,这时候不会创建新的Element或更新Element,只是对位置信息进行校验,如果不一样就更新。
    2). 如果当前的Widget每次都是new的,并且Key不变情况下,会对更新ElementWidget, 执行update方法替换掉当前的widget.
    并且如果当前的ElementStatefulElement,那么他会重写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)。

综上所述,得出结论:

  1. 如果你想一个页面数据保持不变情况, 就复用Widget,不创建新的Widget,他就不会对Element更新或者创建,达到不重复渲染
  2. 如果你想一个页面被摧毁,并且重新走一次新的State,那么更改Key就完事了。
  3. 如果父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方法

由于StatelessElementComponentElement, 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为空的时候,就会调用当前子WidgetcreateElement()
那么在这里,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件事情:

  1. 执行State的createState
  2. 调用super构造, 持有当前Widget
  3. 让State和StatefulElement相互引用
  4. 让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会执行如下几件事情

  1. 执行当前StateinitState()
  2. 执行当前的didChangeDependencies
  3. 执行ComponentElement的_firstBuild,从而执行performRebuild,然后执行StatefulElementbuild(),也就是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件事情:

  1. 将当前Element的_dirty改为true,代表当前的Element需要进行更新页面操作
  2. 调用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到来,都会回调WidgetsBindingdrawFrame方法(详细的Flutter渲染流程可以看看我的这篇文章<<30分钟彻底了解Flutter整个渲染流程>>), 在这篇文章我们知道了, 所有Element的buildOwner都是由WidgetsBinding的buildOwner传递过去的,所以全局唯一。也就是说, 所有需要更新的Element加到_dirtyElements这个列表里面,实质就是加到WidgetsBindingBuildOwner里面。

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不变情况下,会对更新ElementWidget, 执行update方法替换掉当前的widget.然而当前的ElementStatefulElement,那么他会重写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情况,会做如下几件事:

  1. 获取BuildOwner上的Element进行复用,包括RenderObject
  2. 回调Element的activate()方法,并遍历子element都进行activate
  3. 将同类型已存在树上的Element移除
  4. 获取到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的摧毁流程

让我们再次回到ComponentElementupdateChild方法.上面得知,在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会处理以下几件事:

  1. 切断父Element
  2. 调用detachRenderObject移除_slot,从上面得知,这个_slot,会保存索引信息(index),和这个Widget对应的Element(IndexedSlot的Value)。由于Element会持有RenderObject, 所以detachRenderObject顾名思义就是移除RenderObject
  3. 执行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的逻辑。是在buildOwnerfinalizeTree这个方法.


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被移除的流程

  1. Widget被替换或被置空的时候,对应的Element就会被添加到BuildOwner的_InactiveElements的列表里面,此刻Statedeactivate是在这里被回调
  2. 当下个Vsync来的时候,WidgetsFlutterBindingdrawFrame就会将BuildOwner的_InactiveElements的列表所有的Element进行unmount操作
  3. StatefulElement在unmount时候会对State调用dispose()完成最后的生命周期工作

请添加图片描述

整个流程就到这里了,如果这篇文章对你有帮助,请关注🙏,点赞👍,收藏😋三连哦

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值