Flutter开发进阶之瞧瞧BuildOwner

Flutter开发进阶之瞧瞧BuildOwner

上回说到关于Element Tree的构建还缺最后一块拼图,build的重要过程中会调用_element!.markNeedsBuild();,而markNeedsBuild会调用owner!.scheduleBuildFor(this);
在Flutter框架中,BuildOwner负责管理构建过程,它持有当前构建周期的所有相关信息,并协调WidgetElement的转换过程。
Flutter开发进阶
让我们看看BuildOwnerElement中的定义。

/*The object that manages the lifecycle of this element.*/BuildOwner? get owner => _owner;BuildOwner? _owner;void mount(Element? parent, Object? newSlot) {assert(_lifecycleState == _ElementLifecycle.initial);assert(_parent == null);assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);assert(slot == null);_parent = parent;_slot = newSlot;_lifecycleState = _ElementLifecycle.active;_depth = _parent != null ? _parent!.depth + 1 : 1;if (parent != null) {_owner = parent.owner;}assert(owner != null);final Key? key = widget.key;if (key is GlobalKey) {owner!._registerGlobalKey(key, this);}_updateInheritance();attachNotificationTree();}

可知_owner在同一个Element Tree下为唯一。
再来看看BuildOwner的源码。

class BuildOwner {BuildOwner({this.onBuildScheduled, FocusManager? focusManager}): focusManager =focusManager ?? (FocusManager()..registerGlobalHandlers());VoidCallback? onBuildScheduled;final _InactiveElements _inactiveElements = _InactiveElements();final List<Element> _dirtyElements = <Element>[];bool _scheduledFlushDirtyElements = false;bool? _dirtyElementsNeedsResorting;bool get _debugIsInBuildScope => _dirtyElementsNeedsResorting != null;FocusManager focusManager;void scheduleBuildFor(Element element) {assert(element.owner == this);assert(() {if (debugPrintScheduleBuildForStacks) {debugPrintStack(label:'scheduleBuildFor() called for $element${_dirtyElements.contains(element) ? " (ALREADY IN LIST)" : ""}');}if (!element.dirty) {throw FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('scheduleBuildFor() called for a widget that is not marked as dirty.'),element.describeElement('The method was called for the following element'),ErrorDescription('This element is not current marked as dirty. Make sure to set the dirty flag before ''calling scheduleBuildFor().',),ErrorHint('If you did not attempt to call scheduleBuildFor() yourself, then this probably ''indicates a bug in the widgets framework. Please report it:\n''  https:github.com/flutter/flutter/issues/new?template=2_bug.yml',),]);}return true;}());if (element._inDirtyList) {assert(() {if (debugPrintScheduleBuildForStacks) {debugPrintStack(label:'BuildOwner.scheduleBuildFor() called; _dirtyElementsNeedsResorting was $_dirtyElementsNeedsResorting (now true); dirty list is: $_dirtyElements');}if (!_debugIsInBuildScope) {throw FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('BuildOwner.scheduleBuildFor() called inappropriately.'),ErrorHint('The BuildOwner.scheduleBuildFor() method should only be called while the ''buildScope() method is actively rebuilding the widget tree.',),]);}return true;}());_dirtyElementsNeedsResorting = true;return;}if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {_scheduledFlushDirtyElements = true;onBuildScheduled!();}_dirtyElements.add(element);element._inDirtyList = true;assert(() {if (debugPrintScheduleBuildForStacks) {debugPrint('...dirty list is now: $_dirtyElements');}return true;}());}int _debugStateLockLevel = 0;bool get _debugStateLocked => _debugStateLockLevel > 0;bool get debugBuilding => _debugBuilding;bool _debugBuilding = false;Element? _debugCurrentBuildTarget;void lockState(VoidCallback callback) {assert(_debugStateLockLevel >= 0);assert(() {_debugStateLockLevel += 1;return true;}());try {callback();} finally {assert(() {_debugStateLockLevel -= 1;return true;}());}assert(_debugStateLockLevel >= 0);}('vm:notify-debugger-on-exception')void buildScope(Element context, [VoidCallback? callback]) {if (callback == null && _dirtyElements.isEmpty) {return;}assert(_debugStateLockLevel >= 0);assert(!_debugBuilding);assert(() {if (debugPrintBuildScope) {debugPrint('buildScope called with context $context; dirty list is: $_dirtyElements');}_debugStateLockLevel += 1;_debugBuilding = true;return true;}());if (!kReleaseMode) {Map<String, String>? debugTimelineArguments;assert(() {if (debugEnhanceBuildTimelineArguments) {debugTimelineArguments = <String, String>{'dirty count': '${_dirtyElements.length}','dirty list': '$_dirtyElements','lock level': '$_debugStateLockLevel','scope context': '$context',};}return true;}());FlutterTimeline.startSync('BUILD', arguments: debugTimelineArguments);}try {_scheduledFlushDirtyElements = true;if (callback != null) {assert(_debugStateLocked);Element? debugPreviousBuildTarget;assert(() {debugPreviousBuildTarget = _debugCurrentBuildTarget;_debugCurrentBuildTarget = context;return true;}());_dirtyElementsNeedsResorting = false;try {callback();} finally {assert(() {assert(_debugCurrentBuildTarget == context);_debugCurrentBuildTarget = debugPreviousBuildTarget;_debugElementWasRebuilt(context);return true;}());}}_dirtyElements.sort(Element._sort);_dirtyElementsNeedsResorting = false;int dirtyCount = _dirtyElements.length;int index = 0;while (index < dirtyCount) {final Element element = _dirtyElements[index];assert(element._inDirtyList);assert(() {if (element._lifecycleState == _ElementLifecycle.active &&!element._debugIsInScope(context)) {throw FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('Tried to build dirty widget in the wrong build scope.'),ErrorDescription('A widget which was marked as dirty and is still active was scheduled to be built, ''but the current build scope unexpectedly does not contain that widget.',),ErrorHint('Sometimes this is detected when an element is removed from the widget tree, but the ''element somehow did not get marked as inactive. In that case, it might be caused by ''an ancestor element failing to implement visitChildren correctly, thus preventing ''some or all of its descendants from being correctly deactivated.',),DiagnosticsProperty<Element>('The root of the build scope was',context,style: DiagnosticsTreeStyle.errorProperty,),DiagnosticsProperty<Element>('The offending element (which does not appear to be a descendant of the root of the build scope) was',element,style: DiagnosticsTreeStyle.errorProperty,),]);}return true;}());final bool isTimelineTracked =!kReleaseMode && _isProfileBuildsEnabledFor(element.widget);if (isTimelineTracked) {Map<String, String>? debugTimelineArguments;assert(() {if (kDebugMode && debugEnhanceBuildTimelineArguments) {debugTimelineArguments =element.widget.toDiagnosticsNode().toTimelineArguments();}return true;}());FlutterTimeline.startSync('${element.widget.runtimeType}',arguments: debugTimelineArguments,);}try {element.rebuild();} catch (e, stack) {_reportException(ErrorDescription('while rebuilding dirty elements'),e,stack,informationCollector: () => <DiagnosticsNode>[if (kDebugMode && index < _dirtyElements.length)DiagnosticsDebugCreator(DebugCreator(element)),if (index < _dirtyElements.length)element.describeElement('The element being rebuilt at the time was index $index of $dirtyCount')elseErrorHint('The element being rebuilt at the time was index $index of $dirtyCount, but _dirtyElements only had ${_dirtyElements.length} entries. This suggests some confusion in the framework internals.'),],);}if (isTimelineTracked) {FlutterTimeline.finishSync();}index += 1;if (dirtyCount < _dirtyElements.length ||_dirtyElementsNeedsResorting!) {_dirtyElements.sort(Element._sort);_dirtyElementsNeedsResorting = false;dirtyCount = _dirtyElements.length;while (index > 0 && _dirtyElements[index - 1].dirty) {index -= 1;}}}assert(() {if (_dirtyElements.any((Element element) =>element._lifecycleState == _ElementLifecycle.active &&element.dirty)) {throw FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('buildScope missed some dirty elements.'),ErrorHint('This probably indicates that the dirty list should have been resorted but was not.'),Element.describeElements('The list of dirty elements at the end of the buildScope call was',_dirtyElements),]);}return true;}());} finally {for (final Element element in _dirtyElements) {assert(element._inDirtyList);element._inDirtyList = false;}_dirtyElements.clear();_scheduledFlushDirtyElements = false;_dirtyElementsNeedsResorting = null;if (!kReleaseMode) {FlutterTimeline.finishSync();}assert(_debugBuilding);assert(() {_debugBuilding = false;_debugStateLockLevel -= 1;if (debugPrintBuildScope) {debugPrint('buildScope finished');}return true;}());}assert(_debugStateLockLevel >= 0);}Map<Element, Set<GlobalKey>>?_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans;void _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans(Element node, GlobalKey key) {_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans ??=HashMap<Element, Set<GlobalKey>>();final Set<GlobalKey> keys =_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.putIfAbsent(node, () => HashSet<GlobalKey>());keys.add(key);}void _debugElementWasRebuilt(Element node) {_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.remove(node);}final Map<GlobalKey, Element> _globalKeyRegistry = <GlobalKey, Element>{};final Set<Element>? _debugIllFatedElements =kDebugMode ? HashSet<Element>() : null;final Map<Element, Map<Element, GlobalKey>>? _debugGlobalKeyReservations =kDebugMode ? <Element, Map<Element, GlobalKey>>{} : null;int get globalKeyCount => _globalKeyRegistry.length;void _debugRemoveGlobalKeyReservationFor(Element parent, Element child) {assert(() {_debugGlobalKeyReservations?[parent]?.remove(child);return true;}());}void _registerGlobalKey(GlobalKey key, Element element) {assert(() {if (_globalKeyRegistry.containsKey(key)) {final Element oldElement = _globalKeyRegistry[key]!;assert(element.widget.runtimeType != oldElement.widget.runtimeType);_debugIllFatedElements?.add(oldElement);}return true;}());_globalKeyRegistry[key] = element;}void _unregisterGlobalKey(GlobalKey key, Element element) {assert(() {if (_globalKeyRegistry.containsKey(key) &&_globalKeyRegistry[key] != element) {final Element oldElement = _globalKeyRegistry[key]!;assert(element.widget.runtimeType != oldElement.widget.runtimeType);}return true;}());if (_globalKeyRegistry[key] == element) {_globalKeyRegistry.remove(key);}}void _debugReserveGlobalKeyFor(Element parent, Element child, GlobalKey key) {assert(() {_debugGlobalKeyReservations?[parent] ??= <Element, GlobalKey>{};_debugGlobalKeyReservations?[parent]![child] = key;return true;}());}void _debugVerifyGlobalKeyReservation() {assert(() {final Map<GlobalKey, Element> keyToParent = <GlobalKey, Element>{};_debugGlobalKeyReservations?.forEach((Element parent, Map<Element, GlobalKey> childToKey) {if (parent._lifecycleState == _ElementLifecycle.defunct ||parent.renderObject?.attached == false) {return;}childToKey.forEach((Element child, GlobalKey key) {if (child._parent == null) {return;}if (keyToParent.containsKey(key) && keyToParent[key] != parent) {final Element older = keyToParent[key]!;final Element newer = parent;final FlutterError error;if (older.toString() != newer.toString()) {error = FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('Multiple widgets used the same GlobalKey.'),ErrorDescription('The key $key was used by multiple widgets. The parents of those widgets were:\n''- $older\n''- $newer\n''A GlobalKey can only be specified on one widget at a time in the widget tree.',),]);} else {error = FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('Multiple widgets used the same GlobalKey.'),ErrorDescription('The key $key was used by multiple widgets. The parents of those widgets were ''different widgets that both had the following description:\n''  $parent\n''A GlobalKey can only be specified on one widget at a time in the widget tree.',),]);}if (child._parent != older) {older.visitChildren((Element currentChild) {if (currentChild == child) {older.forgetChild(child);}});}if (child._parent != newer) {newer.visitChildren((Element currentChild) {if (currentChild == child) {newer.forgetChild(child);}});}throw error;} else {keyToParent[key] = parent;}});});_debugGlobalKeyReservations?.clear();return true;}());}void _debugVerifyIllFatedPopulation() {assert(() {Map<GlobalKey, Set<Element>>? duplicates;for (final Element elementin _debugIllFatedElements ?? const <Element>{}) {if (element._lifecycleState != _ElementLifecycle.defunct) {assert(element.widget.key != null);final GlobalKey key = element.widget.key! as GlobalKey;assert(_globalKeyRegistry.containsKey(key));duplicates ??= <GlobalKey, Set<Element>>{};Uses ordered set to produce consistent error message.final Set<Element> elements =duplicates.putIfAbsent(key, () => <Element>{});elements.add(element);elements.add(_globalKeyRegistry[key]!);}}_debugIllFatedElements?.clear();if (duplicates != null) {final List<DiagnosticsNode> information = <DiagnosticsNode>[];information.add(ErrorSummary('Multiple widgets used the same GlobalKey.'));for (final GlobalKey key in duplicates.keys) {final Set<Element> elements = duplicates[key]!;information.add(Element.describeElements('The key $key was used by ${elements.length} widgets', elements));}information.add(ErrorDescription('A GlobalKey can only be specified on one widget at a time in the widget tree.'));throw FlutterError.fromParts(information);}return true;}());}('vm:notify-debugger-on-exception')void finalizeTree() {if (!kReleaseMode) {FlutterTimeline.startSync('FINALIZE TREE');}try {lockState(_inactiveElements._unmountAll); assert(() {try {_debugVerifyGlobalKeyReservation();_debugVerifyIllFatedPopulation();if (_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans !=null &&_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.isNotEmpty) {final Set<GlobalKey> keys = HashSet<GlobalKey>();for (final Element elementin _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys) {if (element._lifecycleState != _ElementLifecycle.defunct) {keys.addAll(_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans![element]!);}}if (keys.isNotEmpty) {final Map<String, int> keyStringCount = HashMap<String, int>();for (final String keyin keys.map<String>((GlobalKey key) => key.toString())) {if (keyStringCount.containsKey(key)) {keyStringCount.update(key, (int value) => value + 1);} else {keyStringCount[key] = 1;}}final List<String> keyLabels = <String>[];keyStringCount.forEach((String key, int count) {if (count == 1) {keyLabels.add(key);} else {keyLabels.add('$key ($count different affected keys had this toString representation)');}});final Iterable<Element> elements =_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys;final Map<String, int> elementStringCount =HashMap<String, int>();for (final String element in elements.map<String>((Element element) => element.toString())) {if (elementStringCount.containsKey(element)) {elementStringCount.update(element, (int value) => value + 1);} else {elementStringCount[element] = 1;}}final List<String> elementLabels = <String>[];elementStringCount.forEach((String element, int count) {if (count == 1) {elementLabels.add(element);} else {elementLabels.add('$element ($count different affected elements had this toString representation)');}});assert(keyLabels.isNotEmpty);final String the = keys.length == 1 ? ' the' : '';final String s = keys.length == 1 ? '' : 's';final String were = keys.length == 1 ? 'was' : 'were';final String their = keys.length == 1 ? 'its' : 'their';final String respective =elementLabels.length == 1 ? '' : ' respective';final String those = keys.length == 1 ? 'that' : 'those';final String s2 = elementLabels.length == 1 ? '' : 's';final String those2 =elementLabels.length == 1 ? 'that' : 'those';final String they = elementLabels.length == 1 ? 'it' : 'they';final String think =elementLabels.length == 1 ? 'thinks' : 'think';final String are = elementLabels.length == 1 ? 'is' : 'are';throw FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('Duplicate GlobalKey$s detected in widget tree.'),ErrorDescription('The following GlobalKey$s $were specified multiple times in the widget tree. This will lead to ''parts of the widget tree being truncated unexpectedly, because the second time a key is seen, ''the previous instance is moved to the new location. The key$s $were:\n''- ${keyLabels.join("\n ")}\n''This was determined by noticing that after$the widget$s with the above global key$s $were moved ''out of $their$respective previous parent$s2, $those2 previous parent$s2 never updated during this frame, meaning ''that $they either did not update at all or updated before the widget$s $were moved, in either case ''implying that $they still $think that $they should have a child with $those global key$s.\n''The specific parent$s2 that did not update after having one or more children forcibly removed ''due to GlobalKey reparenting $are:\n''- ${elementLabels.join("\n ")}''\nA GlobalKey can only be specified on one widget at a time in the widget tree.',),]);}}} finally {_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.clear();}return true;}());} catch (e, stack) {_reportException(ErrorSummary('while finalizing the widget tree'), e, stack);} finally {if (!kReleaseMode) {FlutterTimeline.finishSync();}}}void reassemble(Element root) {if (!kReleaseMode) {FlutterTimeline.startSync('Preparing Hot Reload (widgets)');}try {assert(root._parent == null);assert(root.owner == this);root.reassemble();} finally {if (!kReleaseMode) {FlutterTimeline.finishSync();}}}
}

我们忽略debug部分的内容,BuildOwner作为一个基类,void scheduleBuildFor(Element element)方法会将一个需要重新构建的Element添加进_dirtyElements中,然后会Flutter通过调用WidgetsBinding.drawFrame方法,内部会调用buildScope完成重新构建。
综合前几篇文章我们了解到,Widget本身只存储了UI的结构数据,在Widget的初始化过程中会将自身作为参数调用Element的初始化方法创建对应的Element,它是持有WidgetStateBuildOwner的实例,它可以包含其他子Element形成Tree,Element通过State的状态管理去进行对应的生命周期管理,同个Tree下Element对应一个根BuildOwner_owner = parent.owner;它负责协调构建过程,当Element添加进_dirtyElements中时,Flutter循环调用的WidgetsBinding.drawFrameWidgetsFlutterBinding.ensureInitialized()runApp()后会激活的循环)会重新构建Tree渲染到屏幕上。
以上完成build闭环。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/562960.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络层(IP层)

IP协议的本质&#xff1a;有将数据跨网络传输的能力 而用户需要的是将数据从主机A到主机B可靠地跨网络传输 IP的组成&#xff1a;目标网络目标主机 IP由目标网络和目标主机两部分组成&#xff0c;IP报文要进行传输&#xff0c;要先到达目标网络&#xff0c;然后经过路由器转到…

综合知识篇20-基于中间件的开发新技术考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html案例分析篇00-【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例…

div3 E. Binary Search

题目 #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 2e5 5, inf 1e18, maxm 4e4 5, base 397; const i…

一个单生产-多消费模式下无锁方案(ygluu/卢益贵)

一个单生产-多消费模式下无锁方案 ygluu/卢益贵 关键词&#xff1a;生产者-消费者模型、无锁队列、golang、RWMutex 本文介绍一个“单生产(低频)-多消费”模式下的无锁哈希类方案&#xff0c;这个方案的性能优于golang的RWMutex&#xff0c;因为它永远不会因为“写”而导致与…

【Flask】Flask数据迁移操作

Flask数据迁移操作 前提条件 安装第三方包&#xff1a; # ORM pip install flask-sqlalchemy # 数据迁移 pip install flask-migrate # MySQL驱动 pip install pymysql # 安装失败&#xff0c;指定如下镜像源即可 # pip install flask-sqlalchemy https://pypi.tuna.tsinghu…

JVM——运行时数据区

前言 由于JAVA程序是交由JVM执行的&#xff0c;所以我们所说的JAVA内存区域划分也是指的JVM内存区域划分&#xff0c;JAVA程序具体执行的过程如下图所示。首先Java源代码文件会被Java编译器编译为字节码文件&#xff0c;然后由JVM中的类加载器加载各个类的字节码文件&#xff0…

Typecho如何去掉/隐藏index.php

Typecho后台设置永久链接后&#xff0c;会在域名后加上index.php&#xff0c;很多人都接受不了。例如如下网址&#xff1a;https://www.jichun29.cn/index.php/archives/37/&#xff0c;但我们希望最终的形式是这样&#xff1a;https://www.jichun29.cn/archives/37.html。那么…

【数据结构】顺序表习题之移除元素和合并两个有效数组

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《数据结构》 &#x1f389;道阻且长&#xff0c;行则将至 前言 嗨呀&#xff0c;今天的博客是关于顺序表的两道题目&#xff0c;是力扣的移除元素和合并有序数组的题目。 一.移除…

第十四届蓝桥杯大赛软件赛省赛Java大学B组

最近正在备考蓝桥杯&#xff0c;报的java b组&#xff0c;顺便更一下蓝桥的 幸运数字 题目 思路&#xff1a;填空题&#xff0c;暴力即可 import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {static int trans(int x, int y){int …

git基础-查看提交历史

查看提交历史 在创建了多个提交之后&#xff0c;或者如果克隆了一个具有现有提交历史的存储库&#xff0c;可能会想要回顾一下发生了什么。最基本和强大的工具就是 git log 命令。 运行下git log查看下输出状态 默认情况下&#xff0c;不带任何参数运行 git log 命令会以逆时…

使用 VMWare 安装 Android-x86 系统(小白版)

文章目录 VMWare 介绍Android 系统介绍概述最终效果前置步骤开始安装 VMWare 介绍 VMware Workstation是VMware公司开发的一款桌面虚拟化软件。它允许用户在一台物理计算机上同时运行多个操作系统&#xff0c;每个操作系统都在自己的虚拟机中运行。这使得用户可以在同一台计算…

区块链技术下的新篇章:DAPP与消费增值的深度融合

随着区块链技术的持续演进&#xff0c;去中心化应用&#xff08;DAPP&#xff09;正逐渐受到人们的瞩目。DAPP&#xff0c;这种在分布式网络上运行的应用&#xff0c;以其去中心化、安全可靠、透明公开的特性&#xff0c;为用户提供了更为便捷和安全的消费体验。近年来&#xf…