当前位置: 首页 > news >正文

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闭环。

相关文章:

Flutter开发进阶之瞧瞧BuildOwner

Flutter开发进阶之瞧瞧BuildOwner 上回说到关于Element Tree的构建还缺最后一块拼图&#xff0c;build的重要过程中会调用_element!.markNeedsBuild();&#xff0c;而markNeedsBuild会调用owner!.scheduleBuildFor(this);。 在Flutter框架中&#xff0c;BuildOwner负责管理构建…...

大量免费工具使用(提供api接口)

标题: 免费工具集使用 - 简化你的任务 介绍&#xff1a; 在数字化时代&#xff0c;我们经常需要使用各种工具来完成各种任务。本文将介绍一个免费工具集&#xff0c;它提供了多种实用工具&#xff0c;帮助简化你的任务。这些工具可以在网站 https://tool.kertennet.com 上找到…...

网络探测工具Nmap介绍

1. Nmap简介 Nmap是一款用于网络发现和安全审计的网络安全工具。可用于列举网络主机清单、管理服务升级调度、监控主机、监控主机服务运行状况、检测目标主机是否在线和端口开放情况、侦测运行的服务类型及版本信息、侦测操作系统与设备类型等。 2. 命令大纲 3. 命令详细介绍…...

20240319-2-机器学习基础面试题

⽼板给了你⼀个关于癌症检测的数据集&#xff0c;你构建了⼆分类器然后计算了准确率为 98%&#xff0c; 你是否对这个模型很满意&#xff1f;为什么&#xff1f;如果还不算理想&#xff0c;接下来该怎么做&#xff1f; 首先模型主要是找出患有癌症的患者&#xff0c;模型关注的…...

0202矩阵的运算-矩阵及其运算-线性代数

文章目录 一、矩阵的加法二、数与矩阵相乘三、矩阵与矩阵相乘四、矩阵的转置五、方阵的行列式结语 一、矩阵的加法 定义2 设有两个 m n m\times n mn橘子 A ( a i j ) 和 B ( b i j ) A(a_{ij})和B(b_{ij}) A(aij​)和B(bij​),那么矩阵A与B的和记为AB,规定为 A B ( a 11…...

python中的__dict__

类的__dict__返回的是&#xff1a;类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里的&#xff0c; 而实例化对象的&#xff1a;__dict__中存储了一些类中__init__的一些属性值。 import的py文件 __dict__返回的是&#xff1a;__init__的…...

数学分析复习:无穷乘积

文章目录 无穷乘积定义&#xff1a;无穷乘积的收敛性命题&#xff1a;无穷乘积的Cauchy收敛准则正项级数和无穷乘积的联系 本篇文章适合个人复习翻阅&#xff0c;不建议新手入门使用 无穷乘积 设复数列 { a n } n ≥ 1 \{a_n\}_{n\geq 1} {an​}n≥1​&#xff0c;设对任意 …...

02 React 组件使用

import React, { useState } from react;// 定义一个简单的函数式组件 function Counter() {// 使用 useState hook 来创建一个状态变量 count&#xff0c;并提供修改该状态的函数 setCountconst [count, setCount] useState(0);// 在点击按钮时增加计数器的值const increment…...

你就是上帝

你就是上帝&#xff1a;Jv程序员&#xff0c;请你站在上帝或神的角度 1.万物皆有裂缝 按照西方文化&#xff08;宗教神话&#xff0c;古希腊、古罗马等&#xff09;&#xff0c;上帝创建了人&#xff1b; 创建人之前&#xff0c;还创建了人的居所或地盘/栖息地&#xff08;伊…...

Spring Cloud: openFegin使用

文章目录 一、OpenFeign简介二、Springboot集成OpenFeign1、引入依赖2、EnableFeignClients注解&#xff08;1&#xff09;应用&#xff08;2&#xff09;属性解析 3、 FeignClient&#xff08;1&#xff09;应用&#xff08;2&#xff09;属性解析&#xff08;3&#xff09;向…...

流畅的 Python 第二版(GPT 重译)(二)

第三章&#xff1a;字典和集合 Python 基本上是用大量语法糖包装的字典。 Lalo Martins&#xff0c;早期数字游牧民和 Pythonista 我们在所有的 Python 程序中都使用字典。即使不是直接在我们的代码中&#xff0c;也是间接的&#xff0c;因为dict类型是 Python 实现的基本部分。…...

Flutter 旋转动画 线性变化的旋转动画

直接上代码 图片自己添加一张就好了 import dart:math;import package:flutter/material.dart;import package:flutter/animation.dart;void main() > runApp(MyApp()); //旋转动画 class MyApp extends StatelessWidget {overrideWidget build(BuildContext context) {re…...

【Web应用技术基础】HTML(5)——案例1:展示简历信息

样式&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>展示简历信息…...

ethers.js:wallet(创建钱包,导入助记词,导入私钥)

Wallet Wallet类继承了Signer,可以使用私钥作为外部拥有帐户(EOA)的标准对交易和消息进行签名。 npm install ethers@5.4.0// 引入 import {ethers } from ethers创建新钱包 this.provider = new ethers.providers.Web3Provider(window...

面试笔记——Java集合篇

Java集合框架体系 重点&#xff1a;单列集合——ArrayList、LinkedList&#xff1b;双列集合——HashMap、ConcurrentHashMap。 List相关 数组&#xff08;Array&#xff09; 是一种用连续的内存空间存储相同数据类型数据的线性数据结构。 数组获取其他元素&#xff1a; 为什…...

在 IntelliJ IDEA 中使用 Terminal 执行 git log 命令后的退出方法

前言 IntelliJ IDEA 是一款广受欢迎的集成开发环境&#xff0c;它内置了强大的终端工具&#xff0c;使得开发者无需离开IDE就能便捷地执行各种命令行操作&#xff0c;包括使用 Git 进行版本控制。在 IDEA 的 Terminal 中执行 git log 命令时&#xff0c;由于该命令会显示项目的…...

架构整洁之道-读书总结

1 概述 1.1 关于本书 《架构整洁之道》&#xff08;Clean Architecture: A Craftsman’s Guide to Software Structure and Design&#xff09;是由著名的软件工程师Robert C. Martin&#xff08;又称为Uncle Bob&#xff09;所著。这本书提供了软件开发和架构设计的指导原则…...

蓝桥杯学习笔记(贪心)

在很久很久以前&#xff0c;有几个部落居住在平原上&#xff0c;依次编号为1到n。第之个部落的人数为 t 有一年发生了灾荒&#xff0c;年轻的政治家小蓝想要说服所有部落一同应对灾荒&#xff0c;他能通过谈判来说服部落进行联台。 每次谈判&#xff0c;小蓝只能邀请两个部落参…...

【无标题】如何使用 MuLogin 设置代理

如何使用 MuLogin 设置代理 使用 MuLogin 浏览器设置我们的代理&#xff0c;轻松管理多个社交媒体或电子商务帐户。 什么是MuLogin&#xff1f; MuLogin 是一款虚拟反检测浏览器&#xff0c;使用户能够管理多个电子商务、社交媒体和广告帐户&#xff0c;而无需验证码或 IP 禁…...

芒果YOLOv8改进135:主干篇GCNet,统一为全局上下文建模global context结构,即插即用,助力小目标检测,轻量化的同时精度性能涨点

该专栏完整目录链接: 芒果YOLOv8深度改进教程 芒果专栏 基于 GCNet 的改进结构,改进源码教程 | 详情如下🥇 💡本博客 改进源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可 即插即用 结构。博客 包括改进所需的 核心结构代码 文件 论文:https://arxiv.org/a…...

PotPlayer字幕翻译插件:5分钟实现外语影视无障碍观看的终极免费方案

PotPlayer字幕翻译插件&#xff1a;5分钟实现外语影视无障碍观看的终极免费方案 【免费下载链接】PotPlayer_Subtitle_Translate_Baidu PotPlayer 字幕在线翻译插件 - 百度平台 项目地址: https://gitcode.com/gh_mirrors/po/PotPlayer_Subtitle_Translate_Baidu 还在为…...

DamaiHelper:基于Python+Selenium的大麦网自动化抢票解决方案

DamaiHelper&#xff1a;基于PythonSelenium的大麦网自动化抢票解决方案 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 你是否曾经在演唱会门票开售的瞬间&#xff0c;面对"秒光"的票…...

Windows Cleaner深度解析:4步彻底解决C盘空间不足的完整技术方案

Windows Cleaner深度解析&#xff1a;4步彻底解决C盘空间不足的完整技术方案 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服&#xff01; 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner Windows Cleaner是一款完全免费开源的…...

微信小程序逆向分析终极指南:快速掌握wxappUnpacker完整实战技巧

微信小程序逆向分析终极指南&#xff1a;快速掌握wxappUnpacker完整实战技巧 【免费下载链接】wxappUnpacker forked from https://github.com/qwerty472123/wxappUnpacker 项目地址: https://gitcode.com/gh_mirrors/wxappu/wxappUnpacker 作为一名微信小程序开发者&am…...

基于图神经网络的Java空安全注解自动推断技术解析

1. 项目概述&#xff1a;当机器学习遇见类型推断在Java开发中&#xff0c;空指针异常&#xff08;NullPointerException&#xff09;堪称“程序员之敌”&#xff0c;它潜伏在代码的各个角落&#xff0c;是运行时崩溃的常见元凶。为了从根源上解决这个问题&#xff0c;可插拔类型…...

软体机器人跳跃:离散弹性杆仿真与动态分岔原理详解

1. 软体机器人跳跃&#xff1a;从生物灵感走向工程现实如果你观察过一只蚂蚱的起跳&#xff0c;或者一只青蛙的弹射&#xff0c;那种瞬间爆发、姿态优雅的运动&#xff0c;背后是自然界亿万年来优化的高效能量转换机制。传统的刚性机器人&#xff0c;靠着电机、齿轮和连杆&…...

华为防火墙双ISP出口服务器发布避坑指南

1. 这不是配置错误&#xff0c;是网络拓扑与NAT语义的错位“双ISP出口服务器发布”这个组合&#xff0c;在华为防火墙项目现场出现频率极高——电商公司要保障官网高可用&#xff0c;教育平台需兼顾南北用户访问质量&#xff0c;SaaS服务商得满足客户对源IP地址段的合规要求。但…...

【ChatGPT】 BESI 8800系列先进封装键合设备深度拆解、信息图、爆炸图、C++代码框架

深度拆解信息图...

健身行业AI Agent部署失败率高达68%?(2024真实数据复盘与5步合规上线法)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;健身行业AI Agent部署失败率高达68%&#xff1f;——2024真实数据复盘与5步合规上线法 2024年Q2《中国智能健身系统落地白皮书》抽样调研覆盖全国137家连锁健身房及SaaS服务商&#xff0c;结果显示&…...

Frida安卓逆向实战:SELinux适配与Hook可靠性保障

1. 这不是“装个 Frida 就能 Hook”的幻觉&#xff0c;而是安卓逆向真实的第一道门槛很多人点开“Frida 教程”时&#xff0c;心里想的是&#xff1a;“装个 frida-server&#xff0c;跑个 js 脚本&#xff0c;改个登录态&#xff0c;不就完事了&#xff1f;”——我试过三次&a…...