flutter 生命周期源码解析

状态对象具有以下生命周期:

分段理解各个生命周期

生命周期1- createState

分析源码

class StatefulElement extends ComponentElement {
  StatefulElement(StatefulWidget widget)
//可看到在创建StatefulElement 元素的时候创建了 state
      : _state = widget.createState(),
        super(widget) 
//这就是生命周期2的关联
​​​​​​ state._element = this;
//同时把widget 绑定到了state
    state._widget = widget;
  }

生命周期2-  开始State对象与BuildContext相关联

在上一步的构造方法中可以看到已经 state 和element 关联起来了

那是怎么关联BuildContext 的 

看state 源码 找到 BuildContext 对象

BuildContext get context {
//这时候state 和buildcontext关联起来了 
//可以看到 BuildContext 本身就是StatefulElement 
  return _element!;
}

生命周期3- 调用initState

在 ComponentElement源码中 可以看到调用了mount 

mount 就是渲染树插入

mount 的细节描述请看flutter Widget、Element和RenderObject 树的插入源码分析_阿旭哟嘿的博客-CSDN博客

@override
void mount(Element? parent, Object? newSlot) {
  super.mount(parent, newSlot);
  _firstBuild();
}

        //在插入渲染树的时候调用了 initstate 方法

@override
void _firstBuild() {
try {
  //可以看到这时候调用了state.initState()
//注意这时候还只是树的插入步骤,所以平时获取size在这里报错, 因为先插入树,完成后,在开始测量layout 详情看flutter widget layout测量源码解析_阿旭哟嘿的博客-CSDN博客
  final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
     ...其他省略
        }
//这时候调用了生命周期4。
state.didChangeDependencies();
}

生命周期4 -框架调用didChangeDependencies

在上一个方法里面可以看到, 在初始化的后也会调用didChangeDependencies

其他什么时候调用?

先回顾didChangeDependencies 文档描述

当此State对象的依赖项发生更改时调用。

看源码

@pragma('vm:prefer-inline')
void rebuild() {
//如果当前元素的生命周期不等于活跃或者没有脏元素就不构建
//这里的生命周期只是代码内置的运行状态标记
//脏元素表示更新的widget 
//在setstate 时候。会把 Element标记为脏...最后引擎会调用drawFrame的buildOwner!.buildScope(renderViewElement!)在调用element.rebuild();
//细节请看flutter 绘制源码解析_阿旭哟嘿的博客-CSDN博客_flutter源码分析
  if (_lifecycleState != _ElementLifecycle.active || !_dirty)
    return;
  Element? debugPreviousBuildTarget;
  performRebuild();
}

在看 StatefulElement 源码

@override
void performRebuild() {
//重建后会判断是否有改变有改变才会调用
  if (_didChangeDependencies) {
    state.didChangeDependencies();
    _didChangeDependencies = false;
  }
  super.performRebuild();
}

再来分析_didChangeDependencies。

bool _didChangeDependencies = false;

@override
void didChangeDependencies() {
  super.didChangeDependencies();
//发现变为true了
  _didChangeDependencies = true;
}

根据文档提示执行涉及 InheritedWidget的初始化

查看 InheritedWidget  源码

@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
 //发现调用者
  dependent.didChangeDependencies();
}

InheritedWidget 小部件

看描述 小部件的基类,可有效地沿树向下传播信息。

class FrogColor extends InheritedWidget {
  const FrogColor({
    Key? key,
    required this.color,
    required Widget child,
  }) : super(key: key, child: child);

  final Color color;

  static FrogColor of(BuildContext context) {
    final FrogColor? result = context.dependOnInheritedWidgetOfExactType<FrogColor>();
    return result!;
  }

  @override
  bool updateShouldNotify(FrogColor old) => color != old.color;
}

根据上面的信息得出, 此小部件的子类, 在此部件updateShouldNotify 为ture 的情况下子部件的didChangeDependencies 会被调用

生命周期5-​​​​​​​此时,State对象已完全初始化,框架可能会多次调用其​​​​​​​build方法来获取此子树的用户界面描述

ComponentElement 
@pragma('vm:notify-debugger-on-exception')
void performRebuild() {
//开始构建
    built = build();
..其他省略
}

生命周期6-在此期间,父小部件可能会重建并请求更新树中的此位置以显示具有相同 runtimeTypeWidget.key的新小部件

源码分析

@override
//页面更新引擎会触发drawFrame 来重新绘制
void drawFrame() {
   ..其他省略只显示关键代码
//表示根渲染元素
    if (renderViewElement != null)
//建立更新小部件树的范围,并调用给定的“callback”(如果有)
      buildOwner!.buildScope(renderViewElement!);

}

void buildScope(Element context, [ VoidCallback? callback ]) {
//其他省略只显示关键代码
//前面会找出脏元素还是重新build
      try {
//脏元素重新build
        element.rebuild();
      }
}
Element
@pragma('vm:prefer-inline')
void rebuild() {
//隐藏其他代码
//开始执行
  performRebuild();
}
ComponentElement
void performRebuild() {
  Widget? built;
  try {
    //开始获取build 获取新的widget
    built = build();
  } catch (e, stack) {
  } finally {
    //已经获取到了, 就把脏元素置为否
    _dirty = false;
  }
  try {
    //更新新的widget
    _child = updateChild(_child, built, slot);
  } catch (e, stack) {
  }
}
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
  final Element newChild;
  if (child != null) {
    bool hasSameSuperclass = true;
 ​​​​​​​//新的和旧的不一致,所以不会走 
    if (hasSameSuperclass && child.widget == newWidget) {
//其他代码部分省略,只显示关键代码
//细节描述//flutter Widget、Element和RenderObject 树的插入源码分析_阿旭哟嘿的博客-CSDN博客
//hasSameSuperclass 判断组件和元素的类型是否一致
//如果运行类型和key一致就更新,反之不会更新
    } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
      //开始调用
      child.update(newWidget);
      //替换新的子元素
      newChild = child;
    } 
  }
  return newChild;
StatefulElement
@override
void update(StatefulWidget newWidget) {
  super.update(newWidget);
  final StatefulWidget oldWidget = state._widget!;
  _dirty = true;
  state._widget = widget as StatefulWidget;
  try {
  //开始调用didUpdateWidget
    final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
  } finally {
  }
//开始调用build 
  rebuild();
}接着看调用部分源码

生命周期7- 在开发过程中,如果发生热重载, 都会调用reassemble方法

//简单理解就是调试时候用的 不在深入源码
@override
void reassemble() {
  
}

生命周期8 -如果包含State对象的子树从树中移除,则框架调用deactivate方法。

在更新部件中触发

@protected
@pragma('vm:prefer-inline')
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
....省略其他代码只显示关键代
//如果更新后部件一样
    if (hasSameSuperclass && child.widget == newWidget) {
//判断树的位置,如果有改变变成树的位置,重新复制
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      newChild = child;
//如果更新后部件类型一样key一样就调用元素自己更新
    } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
//开始当前元素
//最后每个元素都会依次调用当前方法,去判断新的部件和旧的部件是否一致, 不一致就移除
      child.update(newWidget);
      newChild = child;
    } else {
//如果都不满足直接移除元素(比如newWidget 为null)
//里面会移除渲染树, 并添加当前元素到非活动元素集合
      deactivateChild(child);
//这时候会触发 state.activate() 已经和父元素绑定
      newChild = inflateWidget(newWidget, newSlot);
    }
  return newChild;
}
如果是多子部件的话,流程会有区别
MultiChildRenderObjectElement
@override
void update(MultiChildRenderObjectWidget newWidget) {
  super.update(newWidget);
  final MultiChildRenderObjectWidget multiChildRenderObjectWidget = widget as MultiChildRenderObjectWidget;
//开始更新子元素
  _children = updateChildren(_children, multiChildRenderObjectWidget.children, forgottenChildren: _forgottenChildren);
  _forgottenChildren.clear();
}

@protected
List<Element> updateChildren(List<Element> oldChildren, List<Widget> newWidgets, { Set<Element>? forgottenChildren, List<Object?>? slots }) {
其他省略....
int newChildrenTop = 0;
int oldChildrenTop = 0;
int newChildrenBottom = newWidgets.length - 1;
int oldChildrenBottom = oldChildren.length - 1
Element? previousChild;
//从顶部遍历列表,同步节点,直到不再有匹配节点. 
//比如 老的1 2 3 4 5 新的 1 2 7 8 5 这时候循环到7的时候就会停止 3 和7 不一致, 1和2 就会同步上去  这时候顶部坐标移动到2
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
//忽略指定的旧元素
  final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
  final Widget newWidget = newWidgets[newChildrenTop];
//如果新老相同节点的小部件,类型不一致 直接终止循环 ,那头部扫描只包含之前的扫描的
  if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
    break;
//每单个元素再次递归调更新自己的子部件,最后返回新的元素
  final Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
//新的子部件给值
  newChildren[newChildrenTop] = newChild;
//上一个子元素
  previousChild = newChild;
  newChildrenTop += 1;
  oldChildrenTop += 1;
}

//从底部遍历列表,不同步节点,直到你没有不再有匹配的节点
比如 老的1 2 3 4 5 新的 1 2 7 8 5 这时候倒叙循环。 循环到 4的时候就会停止  4和8不一致。 5不会同步上去,但是底部的下标会移动到 3
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
  final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenBottom]);
  final Widget newWidget = newWidgets[newChildrenBottom];
  if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
    break;
  oldChildrenBottom -= 1;
  newChildrenBottom -= 1;
}

//遍历旧列表的变窄部分,得到列表 键和同步 null 与非键项
//剩下来的 顶部2 底部3 开始循环2次
final bool haveOldChildren = oldChildrenTop <= oldChildrenBottom;
Map<Key, Element>? oldKeyedChildren;
if (haveOldChildren) {
  oldKeyedChildren = <Key, Element>{};
  while (oldChildrenTop <= oldChildrenBottom) {
    final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
    if (oldChild != null) {
      if (oldChild.widget.key != null)
        //缓存旧的有key的部件, 不走deactivate
        oldKeyedChildren[oldChild.widget.key!] = oldChild;
      else
        //移除部件 (调用了生命周期的deactivate )
        //里面会移除渲染树, 并添加当前元素到非活动元素集合
        deactivateChild(oldChild);
    }
//下标移动
    oldChildrenTop += 1;
  }
}

//新的 顶部还是2 底部还是3
//向前移动新列表的缩小部分
while (newChildrenTop <= newChildrenBottom) {
  Element? oldChild;
//获取部件
  final Widget newWidget = newWidgets[newChildrenTop];
  if (haveOldChildren) {
    final Key? key = newWidget.key;
    if (key != null) {
   //获取旧的缓存对应的key的部件
      oldChild = oldKeyedChildren![key];
      if (oldChild != null) {
        if (Widget.canUpdate(oldChild.widget, newWidget)) {
       //移除缓存
          oldKeyedChildren.remove(key);
        } else {
//类型不一样
          oldChild = null;
        }
      }
    }
  }
//旧部件和新部件更新
//slotFor 表示上一个元素的位置和值
  final Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
//更新对应的新部件
  newChildren[newChildrenTop] = newChild;
//更新上一个
  previousChild = newChild;
//下标移动
  newChildrenTop += 1;
}

//这时候就把最开始底部移动的下标恢复
newChildrenBottom = newWidgets.length - 1;
oldChildrenBottom = oldChildren.length - 1;
//再次遍历列表底部,同步节点
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
  final Element oldChild = oldChildren[oldChildrenTop];
  final Widget newWidget = newWidgets[newChildrenTop];
  final Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
  newChildren[newChildrenTop] = newChild;
  previousChild = newChild;
  newChildrenTop += 1;
  oldChildrenTop += 1;
}

//同步缓存里面还剩下的
//forgottenChildren 表示忽略的元素
if (haveOldChildren && oldKeyedChildren!.isNotEmpty) {
  for (final Element oldChild in oldKeyedChildren.values) {
    if (forgottenChildren == null || !forgottenChildren.contains(oldChild))
      deactivateChild(oldChild);
  }
}
assert(newChildren.every((Element element) => element is! _NullElement));
return newChildren;

生命周期9-此时,框架可能会将此子树重新插入树的另一部分

参考上一个生命周期, 多个子元素组件,  带keuy子元素只是替换的位置, 就不会触发deactivate

生命周期10-如果框架在当前动画帧结束时没有重新插入这个子树,框架将调用dispose,这表明这个State对象永远不会再次构建

void drawFrame() {
  try {
    if (renderViewElement != null)
      buildOwner!.buildScope(renderViewElement!);
    super.drawFrame();
//通过卸载任何不存在的元素来完成元素构建过程
    buildOwner!.finalizeTree();
  } 
}
void finalizeTree() {
  if (!kReleaseMode) {
    Timeline.startSync('FINALIZE TREE', arguments: timelineArgumentsIndicatingLandmarkEvent);
  }
  try {

//建立一个禁止调用 [State.setState] 的范围 并且回调
//这时候就会调用非活动元素集合
    lockState(_inactiveElements._unmountAll);

...其他省略

void _unmountAll() {
  _locked = true;
  final List<Element> elements = _elements.toList()..sort(Element._sort);
  _elements.clear();
  try {
//开始循环 _unmount
    elements.reversed.forEach(_unmount);
  } finally {
    _locked = false;
  }
}

void _unmount(Element element) 
  element.visitChildren((Element child) {
    _unmount(child);
  });
//开始分离
  element.unmount();
}
StatefulElement
@override
void unmount() {
  super.unmount();
//调用dispose
  state.dispose();
  state._element = null;
  _state = null;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值