OpenHarmony实战开发-状态变量组件定位工具实践

news2024/11/27 0:36:30

概述

自定义组件中的变量被状态装饰器(@State,@Prop等)装饰后成为状态变量,而状态变量的改变会引起使用该变量的UI组件渲染刷新。状态变量的不合理使用可能会带来冗余刷新等性能问题。开发者可以使用状态变量组件定位工具获取状态管理相关信息,例如自定义组件拥有的状态变量、状态变量的同步对象和关联组件等,了解状态变量影响UI的范围,写出高性能应用代码。

本文将通过场景示例为开发者提供状态变量组件定位工具的实践指导,并对工具相关的调试命令和输出结果作解释说明。

使用流程

状态变量组件定位工具是通过使用HiDumper工具中特定的命令来获取组件和状态管理相关的信息。开发者可以使用工具查看指定自定义组件拥有的状态变量信息,了解其中各个状态变量影响的组件范围。

工具的使用流程可以分为以下几步:

1、设备上打开组件所在页面。

2、获取应用的窗口Id。

3、基于应用窗口Id获取应用的自定义组件树,找到目标组件和它的节点Id。

4、根据上一步获取的组件节点Id获取组件拥有的状态变量信息。

5、找到目标状态变量,查看它影响的组件范围。

在明确目标组件包含的状态变量和变量影响的组件刷新范围后,开发者可以根据需求分析是否存在状态变量的不合理使用,并对相应代码分析和优化。

场景示例

下面通过一个点击按钮更改状态变量引起组件刷新的场景示例,为开发者提供工具的实践指导。场景示例仅展示部分关键代码。

在以下代码中,创建了自定义组件ComponentA、SpecialImage,每个组件都拥有一些状态变量和UI组件。组件ComponentA中存在Move和Scale两个按钮,在按钮的点击回调中改变状态变量的值刷新相应的组件。

// 常量声明
// ... 
// 样式属性类  
class UIStyle {  
  public translateX: number = 0;  
  public translateY: number = 0;  
  public scaleX: number = 0.3;  
  public scaleY: number = 0.3;  
}  
@Component  
struct ComponentA {  
  @Link uiStyle: UIStyle; // uiStyle的属性被多个组件使用  
  build() {  
    Column() {  
      // 使用状态变量的组件  
      SpecialImage({ specialImageUiStyle: this.uiStyle })  
      Stack() {  
        Column() {  
          Image($r('app.media.icon'))  
            .scale({  
              x: this.uiStyle.scaleX,  
              y: this.uiStyle.scaleY  
            })  
        }  
        Stack() {  
          Text('Hello World')  
        }  
      }  
      .translate({  
        x: this.uiStyle.translateX,  
        y: this.uiStyle.translateY  
      })  
  
      // 通过按钮点击回调修改状态变量的值,引起相应的组件刷新  
      Column() {  
        Button('Move')  
          .onClick(() => {  
            animateTo({ duration: animationDuration }, () => {  
              this.uiStyle.translateY = (this.uiStyle.translateY + translateYChangeValue) % translateYChangeRange;  
            })  
          })  
        Button('Scale')  
          .onClick(() => {  
            this.uiStyle.scaleX = (this.uiStyle.scaleX + scaleXChangeValue) % scaleXChangeRange;  
          })  
      }  
    }  
  }  
}  
@Component  
struct SpecialImage {  
  @Link specialImageUiStyle: UIStyle;  
  private opacityNum: number = 0.5; // 默认透明度  
  private isRenderSpecialImage(): number {  
    // Image每次渲染时透明度增加0.1, 在0-1之间循环  
    this.opacityNum = (this.opacityNum + opacityChangeValue) % opacityChangeRange;  
    return this.opacityNum;  
  }  
  build() {  
    Column() {  
      Image($r('app.media.icon'))  
        .scale({  
          x: this.specialImageUiStyle.scaleX,  
          y: this.specialImageUiStyle.scaleY  
        })  
        .opacity(this.isRenderSpecialImage())  
      Text("SpecialImage")  
    }  
  }  
}  
// 页面根组件,ComponentA的父组件

运行上述示例并分别点击按钮,可以看到点击Move按钮和Scale按钮时组件SpecialImage都出现了刷新,运行效果图如下。
在这里插入图片描述

下面以自定义组件ComponentA和其中的状态变量uiStyle为例介绍工具的使用过程。

1、首先在设备上打开应用,进入ComponentA组件所在的页面。

2、使用以下命令获取示例应用的窗口Id。当前运行的示例应用包名为performancelibrary,可以在输出结果中找到对应窗口名performancelibrary0的WinId,即为应用的窗口Id。或者当应用正处于前台运行时,Focus window的值就是应用的窗口Id。此处示例应用的窗口Id为11,后面的流程中使用的命令都需要指定窗口Id。

hdc shell "hidumper -s WindowManagerService -a '-a'"

在这里插入图片描述

3、基于上一步获取的窗口Id 11,使用-viewHierarchy命令携带-r 参数递归打印应用的自定义组件树。从结果中找到目标组件ComponentA,后面括号中的内容即为组件ComponentA的节点Id 70。

hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -viewHierarchy -r'"
-----------------ViewPU Hierarchy-----------------  
[-viewHierarchy, viewId=4, isRecursive=true]  
|--Index[4]  
-----------------ViewPU Hierarchy-----------------  
[-viewHierarchy, viewId=53, isRecursive=true]  
|--DFXStateManagementPage[53]  
  |--DFXStateManagementHome[55]  
-----------------ViewPU Hierarchy-----------------  
[-viewHierarchy, viewId=65, isRecursive=true]  
|--DFXStateBeforeOptimizationPage[65]  
  |--DFXStateBeforeOptimization[67]  
    |--ComponentA[70]  
      |--SpecialImage[73]

4、使用命令-stateVariables携带参数-viewId(参数的值为ComponentA的节点Id)获取自定义组件ComponentA中的状态变量信息。结果显示ComponentA拥有@Link/@Consume类型的状态变量uiStyle。每条状态变量的详细信息都包含状态变量的所属组件、同步对象和关联组件。

hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -stateVariables -viewId=70'"
--------------ViewPU State Variables--------------  
[-stateVariables, viewId=70, isRecursive=false]  
|--ComponentA[70]  
  @Link/@Consume (class SynchedPropertyTwoWayPU) 'uiStyle'[71]  
  |--Owned by @Component 'ComponentA'[70]  
  |--Sync peers: {  
    @Link/@Consume (class SynchedPropertyTwoWayPU) 'specialImageUiStyle'[74] <@Component 'SpecialImage'[73]>  
  }  
  |--Dependent components: 2 elmtIds: 'Stack[75]', 'Image[77]'

5、以状态变量uiStyle为例。

① Sync peers表示uiStyle在自定义组件SpecialImage中存在@Link/@Consume类型的状态变量specialImageUiStyle订阅数据变化。

② Dependent components表示在ComponentA组件中存在组件Stack[79]和Image[81]使用了状态变量uiStyle,关联组件的数量为2。

所以当uiStyle变化时,影响的组件范围为自定义组件SpecialImage以及系统组件Stack[79]和Image[81]。
在这里插入图片描述

示例中组件SpecialImage仅使用了uiStyle传递到specialImageUiStyle中的属性scaleX、scaleY,但是点击Move按钮修改uiStyle中的属性translateY时引起的uiStyle变化也会导致组件SpecialImage的刷新,所以可以将uiStyle中的属性scaleX、scaleY提取到状态变量scaleStyle中,属性translateX和translateY提取到状态变量translateStyle中,仅传递scaleStyle给组件SpecialImage,避免不必要的刷新。

由于提取后存在Class的嵌套,所以需要使用@Observed/@ObjectLink装饰器装饰相应的Class和状态变量。修改后的部分代码如下。

// 常量声明
// ...
// 样式属性类,嵌套ScaleStyle, TranslateStyle  
@Observed  
class UIStyle {  
  translateStyle: TranslateStyle = new TranslateStyle();  
  scaleStyle: ScaleStyle = new ScaleStyle();  
}  
// 缩放属性类  
@Observed  
class ScaleStyle {  
  public scaleX: number = 0.3;  
  public scaleY: number = 0.3;  
}  
// 位移属性类  
@Observed  
class TranslateStyle {  
  public translateX: number = 0;  
  public translateY: number = 0;  
}  
@Component  
struct ComponentA {  
  @ObjectLink scaleStyle: ScaleStyle;  
  @ObjectLink translateStyle: TranslateStyle;  
  
  build() {  
    Column() {  
      SpecialImage({  
        specialImageScaleStyle: this.scaleStyle  
      })  
      // 其他UI组件  
    }  
  }  
}  
  
@Component  
struct SpecialImage {  
  @Link specialImageScaleStyle: ScaleStyle;  
  // isRenderSpecialImage函数  
  build() {  
    Column() {  
      Image($r('app.media.icon'))  
        .scale({  
          x: this.specialImageScaleStyle.scaleX,  
          y: this.specialImageScaleStyle.scaleY  
        })  
        .opacity(this.isRenderSpecialImage())   
      Text("SpecialImage")  
    }
  }  
}
// 页面根组件,ComponentA的父组件

修改后的示例运行效果图如下,只有点击Scale按钮时SpecialImage产生刷新现象,点击Move按钮时SpecialImage不会刷新。
在这里插入图片描述

可以使用上文步骤再次获取ComponentA组件的状态变量信息如下,可以看到ComponentA中状态变量scaleStyle影响组件SpecialImage[74]和Image[78],状态变量translateStyle影响组件Stack[76],translateStyle的变化不会再导致SpecialImage的刷新。

--------------ViewPU State Variables--------------  
[-stateVariables, viewId=70, isRecursive=false]  
  
|--ComponentA[70]  
  @ObjectLink (class SynchedPropertyNestedObjectPU) 'scaleStyle'[71]  
  |--Owned by @Component 'ComponentA'[70]  
  |--Sync peers: {  
    @Link/@Consume (class SynchedPropertyTwoWayPU) 'specialImageScaleStyle'[75] <@Component 'SpecialImage'[74]>  
  }  
  |--Dependent components: 1 elmtIds: 'Image[78]'  
  @ObjectLink (class SynchedPropertyNestedObjectPU) 'translateStyle'[72]  
  |--Owned by @Component 'ComponentA'[70]  
  |--Sync peers: none  
  |--Dependent components: 1 elmtIds: 'Stack[76]'

调试命令和输出详解

下面通过一个自定义组件三层嵌套的简单示例程序,对工具的调试命令和输出结果作详细说明。

@Entry  
@Component  
struct Index {  
  @State indexMessage: string = 'Hello World';  
  build() {  
    Row() {  
      Column() {  
        Text(this.indexMessage)  
        ComponentA({ componentAMessage: this.indexMessage })  
      }  
      .width('100%')  
    }  
    .height('100%')  
  }  
}  
@Component  
struct ComponentA {  
  @Link componentAMessage: string;  
  build() {  
    Column() {  
      ComponentB({ componentBMessage: this.componentAMessage })  
    }  
  }  
}  
@Component  
struct ComponentB {  
  @Link componentBMessage: string;  
  build() {  
    Column() {  
      Text(this.componentBMessage)  
    }  
  }  
}

1、查看应用窗口Id。可以通过窗口列表中应用的WindowName(示例应用的包名为dfxdemo,默认的WindowName为dfxdemo0)找到其WinId,即应用窗口Id。结果中的Focus window为当前界面展示的窗口Id。当应用处于前台运行时,Focus window的值即为应用窗口Id。

hdc shell "hidumper -s WindowManagerService -a '-a'"

2、打印自定义组件树。

默认只打印根节点和它子级的自定义组件。其中 11 表示查看的窗口Id,可使用查看应用窗口Id命令获取。结果中形如Index[4]格式的,前面为自定义组件的名称,[]中的数字为组件的节点Id。

hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -viewHierarchy'"
-----------------ViewPU Hierarchy-----------------  
[-viewHierarchy, viewId=4, isRecursive=false]  
|--Index[4]  
  |--ComponentA[9]

携带-viewId参数可以打印指定viewId的自定义组件和该组件子级的自定义组件。只有自定义组件的节点Id可以作为参数-viewId的值使用。以-viewId=9为例,可以获取到viewId为9的自定义组件ComponentA和它子级的自定义组件ComponentB。

hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -viewHierarchy -viewId=9'"
-----------------ViewPU Hierarchy-----------------  
[-viewHierarchy, viewId=9, isRecursive=false]  
|--ComponentA[9]  
  |--ComponentB[12]

携带-r参数从根节点开始递归打印自定义组件树。例如下面命令的输出结果为根节点Index和它拥有的每一级自定义组件,以树状结构展示。

hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -viewHierarchy -r'"
-----------------ViewPU Hierarchy-----------------  
[-viewHierarchy, viewId=4, isRecursive=true]  
|--Index[4]  
  |--ComponentA[9]  
    |--ComponentB[12]

3、打印自定义组件的状态变量信息,每条信息包含该变量的所属组件、Sync peers(同步对象)和Dependent components(关联组件)。当该状态变量改变时,它的Dependent components和Sync peers的Dependent components都是脏节点。默认打印根节点。

hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -stateVariables'"
--------------ViewPU State Variables--------------  
--Index[4]  
  @State/@Provide (class ObservedPropertyPU) 'indexMessage'[5]  
  |--Owned by @Component 'Index'[4]  
  |--Sync peers: {  
    @Link/@Consume (class SynchedPropertyTwoWayPU) 'componentAMessage'[9] <@Component 'ComponentA'[8]>  
  }  
  |--Dependent components: 1 elmtIds: 'Text[8]'
  • @State/@Provide:状态变量的装饰器类型。
  • ‘message’[5]:状态变量的名称和节点Id,但是不能使用-viewId=5来获取相应的dump信息。
  • Owned by @Component ‘Index’[4]:状态变量所属组件。
  • Sync peers: 要同步的其他状态变量和其所属的组件。
  • Dependent components:当前状态变量在此组件中关联的节点Id,即依赖该变量的系统或自定义组件的节点Id。

注意:

-stateVariables只支持打印指定viewId的状态变量信息,不支持递归打印。所以只能获取单个自定义组件的状态变量信息进行逐级分析,目前无法从全局查看某个状态变量影响到的所有组件。

4、打印所有信息,包含自定义组件树和状态变量信息。未指定节点时默认打印根节点,携带-viewId参数打印指定节点信息,携带-r参数递归打印。

hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -dumpAll'"

输出结果如下图:
在这里插入图片描述

1、自定义组件树,对应命令-viewHierarchy。

2、状态变量信息,对应命令-stateVariables。

dumpAll命令携带-r和-viewId参数时,输出结果中对应各个命令的部分与单独使用该命令携带相应参数时的结果相同。

如果大家还没有掌握鸿蒙,现在想要在最短的时间里吃透它,我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程》以及《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

OpenHarmony APP开发教程步骤:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

《鸿蒙开发学习手册》:

如何快速入门:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.基本概念
2.构建第一个ArkTS应用
3.……

在这里插入图片描述

开发基础知识:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙生态应用开发白皮书V2.0PDF:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JdbcTemplate详解

1 概述 为了使JDBC更加易于使用&#xff0c;Spring在JDBC API上定义了一个抽象层&#xff0c;以此建立一个JDBC存取框架。 作为Spring JDBC框架的核心&#xff0c;JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法&#xff0c;通过这种方式&#xff0c;可以在尽可能保留…

【数据结构(邓俊辉)学习笔记】向量02——动态空间管理

文章目录 1. 概述2. 静态空间管理缺点3. 动态空间管理3.1 扩容3.1.1 如何实现扩容3.1.2 扩容算法3.1.3 容量递增策略 VS 容量倍增策略3.1.3.1 容量倍增策略分摊分析3.1.3.2 容量递增策略分摊分析3.1.3.3 结果对比 3.2缩容3.2.1 动态缩容算法实现3.2.2 动态缩容算法时间复杂度 4…

Sui主网升级至V1.23.1版本

其他升级要点如下所示&#xff1a; #17126 协议&#xff1a;Deepbook的更改将被还原。 #16673 开发者可能会看到更多编译器诊断&#xff0c;因为选择的解析错误不再阻止编译&#xff0c;并且编译器的诊断会到达后续编译阶段&#xff0c;其中可能会生成额外的诊断。 #16966…

SQLite FTS5 扩展(三十)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite的知名用户(二十九) 下一篇:SQLite 的命令行 Shell(三十一&#xff09; 1. FTS5概述 FTS5 是一个 SQLite 虚拟表模块&#xff0c;它为数据库应用程序提供全文搜索功能。在最基本的形式中&#xff0c; 全文搜索引擎允许…

【LAMMPS学习】八、基础知识(4.5)TIP5P水模型

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

JVS物联网平台驱动网关管理:智能化与自动化的探索

驱动网关的管理 驱动网关是对驱动进程进行生命周期的管理&#xff0c;包括驱动进程的创建、启停、更新、结束等管理功能。 如下图所示&#xff1a; 驱动代理程序是驱动网关的管理程序&#xff0c;每个驱动网关对应于一个驱动代理程序&#xff0c;驱动代理程序初始版本是安装在…

Nginx解决跨域访问难题:轻松实现跨域资源共享!

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 跨域资源共享&#xff08;CORS&#xff0c;Cross-Origin Resource Sharing&#xff09;是一种网络浏览器的安全功能&#xff0c;它限制了一个源&#xff08;域、协议和端口…

算法----BF算法KMP算法

请想象一个情景&#xff1a; 当你脑海中突然浮现出一个词&#xff0c;你该怎么去找到这个词的有关内容&#xff1f; 打开我们浏览器的搜索框&#xff0c;输入你想的这个词&#xff0c;然后点击Enter。浏览器就会自动搜索与该词匹配的内容。 这个过程实际上可以简化成以下形式…

常用的时间序列分析方法总结和代码示例

时间序列是最流行的数据类型之一。视频&#xff0c;图像&#xff0c;像素&#xff0c;信号&#xff0c;任何有时间成分的东西都可以转化为时间序列。 在本文中将在分析时间序列时使用的常见的处理方法。这些方法可以帮助你获得有关数据本身的见解&#xff0c;为建模做好准备并…

删除docker的容器与镜像

如果您想要卸载通过 docker pull influxdb 命令下载的 InfluxDB 容器&#xff0c;您需要执行以下步骤&#xff1a; 1. **停止正在运行的 InfluxDB 容器**&#xff1a; 首先&#xff0c;您需要停止任何正在运行的 InfluxDB 容器。您可以使用以下命令来查找正在运行的 InfluxD…

15 JavaScript学习:循环

JavaScript循环语句概念和分类 JavaScript中的循环语句用于重复执行特定的代码块&#xff0c;直到指定的条件不再满足。下面是JavaScript中常用的循环语句以及它们的概念和分类&#xff1a; for 循环&#xff1a;for循环通过指定起始条件、循环条件和递增/递减步长来进行迭代。…

C++程序在Windows平台上各种定位内存泄漏的方法

一、前言 在Linux平台上有valgrind可以非常方便的帮助我们定位内存泄漏&#xff0c;因为Linux在开发领域的使用场景大多是跑服务器&#xff0c;再加上它的开源属性&#xff0c;相对而言&#xff0c;处理问题容易形成“统一”的标准。而在Windows平台&#xff0c;服务器和客户端…

云计算时代:SFP、SFP+、SFP28、QSFP+和QSFP28光纤模块详解

随着数据中心的快速发展和云计算的广泛应用&#xff0c;高速、高效率的光纤网络传输成为关键需求。在众多光纤模块中&#xff0c;SFP、SFP、SFP28、QSFP和QSFP28是最常见的几种类型。本文将为您详细解析这几种光纤模块之间的区别&#xff0c;帮助您更好地了解和选择适合自己需求…

VS2019中配置C++ OpenCV 4.5.4完整指南

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

C# 图像处理 添加水印

方法1&#xff0c;使用自带的画刷进行绘制水印 示例代码 public partial class Form1 : Form{public Form1(){InitializeComponent();}string photoPathstring.Empty;Bitmap image null;private void button1_Click(object sender, EventArgs e) //选择照片{OpenFileDialog d…

JavaWeb-自学JSP组件报告

自学JSP组件报告 一、组件资源及作用 1. commons-fileupload-1.2.2.jar 组件作用&#xff1a;用于处理HTTP文件上传请求&#xff0c;提供了文件上传的解析和存储功能。它允许开发者在Web应用中轻松实现文件上传功能。 2. commons-io-2.4.jar 组件作用&#xff1a;提供了一…

免单优选:电商销售新模式,低门槛高回报引领消费热潮

免单优选&#xff0c;一种颠覆传统的电商销售新模式&#xff0c;其核心在于通过价格优化策略、创新的奖励机制以及社交网络的深度应用&#xff0c;来激发消费者的购买欲望&#xff0c;进而推动销售实现快速增长。 一、坚守法律底线&#xff0c;打造透明奖励体系 免单优选模式坚…

POCEXP编写—POC编写实战

POC&EXP编写—POC编写实战 1. 概念引入2. POC编写2.1. GET与POST2.2. thinkcmf文件包含(Get请求)2.2.1. POC验证2.2.1.1. 漏洞存在复现 2.2.2. 编写POC2.2.2.1. 脚本报错2.2.2.2. 脚本美化2.2.2.3. 脚本测试2.2.2.4. 注意事项 2.3. Struts2 S2-061 远程命令执行漏洞(Get请求…

Hot 100总结【leetcode】

文章目录 哈希1. 1 两数之和2. 49 字母异或词分组3. 128 最长连续序列4. 283 移动零5. 11 盛最多水的容器6. 15 三数之和7. 42 接雨水 滑动窗口8. 3 无重复的最长子串9. 438 找到字符串中所有字母异位词子串10. 560 和为k的子数组11. 239 滑动窗口最大值12. 76 最小覆盖子串 普…

SpringBoot + kotlin 协程小记

前言&#xff1a; Kotlin 协程是基于 Coroutine 实现的&#xff0c;其设计目的是简化异步编程。协程提供了一种方式&#xff0c;可以在一个线程上写起来像是在多个线程中执行。 协程的基本概念&#xff1a; 协程是轻量级的&#xff0c;不会创建新的线程。 协程会挂起当前的协…