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

HarmonyOS开发实战( Beta5版)高负载组件的渲染实践规范

简介

在应用开发中,有的页面需要在列表中加载大量的数据,就会导致组件数量较多或者嵌套层级较深,从而引起组件负载加重,绘制耗时增长。虽然可以通过组件复用避免组件重复创建,但是如果每个列表项中包含的组件较多,在转场或者列表滑动的时候列表项就会一次性加载大量的数据,可能引起卡顿掉帧等性能问题。

转场场景

由于业务需求,从当前页面进入一个新页面时,会有转场动画播放,并且在动画首帧中加载新页面所需要的数据。如果数据量较多,那么动画首帧的响应时延就会变长,导致后面的动画帧延迟播放,产生卡顿的情况。

动画

解决思路

既然转场时一次性加载大量的数据会导致卡顿情况,那么将数据拆分成多份并分批次进行加载就是一种解决思路。ArkTS中提供了DisplaySync(可变帧率),可以设置帧回调监听,让开发者在不同的帧中进行一些操作,这样就可以将本来在一帧中加载的数据分到多帧中加载,减少动画首帧的响应时间,降低完成时延。

常规代码

在自定义列表组件中一次性加载全部数据,可参考组件堆叠场景中的具体实现。

// CommonAppDevelopment/feature/componentstack/src/main/ets/view/ProductList.ets
@Component
export struct ProductList {private productData: ProductDataSource = new ProductDataSource();aboutToAppear(): void {this.productData.pushData(PRODUCT_DATA)}build() {WaterFlow() {LazyForEach(this.productData, (item: ProductDataModel) => {FlowItem() {ItemView({ item: item })}}, (item: ProductDataModel) => item.id.toString())}...}
}

这段代码里,在aboutToAppear()接口中,将数据放入productData中,并通过瀑布流加载。编译运行后,可以通过Trace图看到,转场动画的首帧(235970)耗时21ms左右,这是因为在点击进入页面时将6条数据全部放入瀑布流,在235970帧中需要计算6个子组件的尺寸,导致了响应时间增长。如果数据量更大,那么这个时间会变得更长,动画的卡顿效果就会更加明显。

image-20240717183013984

优化代码

在aboutToAppear()接口中添加DisplaySync的帧回调,并将数据拆分进行加载。

@Component
export struct ProductList {private productData: ProductDataSource = new ProductDataSource();private displaySync: displaySync.DisplaySync | undefined = undefined;private frame: number = 1;aboutToAppear(): void {// 创建DisplaySync对象this.displaySync = displaySync.create();// 设置期望帧率const range: ExpectedFrameRateRange = {expected: 120,min: 60,max: 120};this.displaySync.setExpectedFrameRateRange(range);// 添加帧回调this.displaySync.on("frame", () => {if (this.frame === 1) {hiTraceMeter.startTrace('firstFrame', 1);this.productData.pushData(PRODUCT_DATA.slice(0, 2))this.frame += 1;hiTraceMeter.finishTrace('firstFrame', 1);} else if (this.frame === 2) {hiTraceMeter.startTrace('secondFrame', 2);this.productData.pushData(PRODUCT_DATA.slice(2, PRODUCT_DATA.length));hiTraceMeter.finishTrace('secondFrame', 2);this.frame += 1;this.displaySync?.stop();}});// 开启帧回调监听this.displaySync.start();}aboutToDisappear(): void {// 页面销毁时需要停止帧回调this.displaySync?.stop();}build() {// TODO: 知识点:瀑布流容器,由“行”和“列”分割的单元格所组成,通过容器自身的排列规则,将不同大小的“项目”自上而下,如瀑布般紧密布局。WaterFlow() {LazyForEach(this.productData, (item: ProductDataModel) => {FlowItem() {ItemView({ item: item })}}, (item: ProductDataModel) => item.id.toString())}.nestedScroll({scrollForward: NestedScrollMode.PARENT_FIRST,scrollBackward: NestedScrollMode.SELF_FIRST}).columnsTemplate("1fr 1fr").columnsGap(COLUMNSGAP).rowsGap(ROWSGAP).padding({ bottom: $r("app.integer.component_stack_water_flow_padding_bottom") })}
}

在这段代码中,aboutToAppear()接口中并没有一次性加载全部数据,而是将数据拆分,在帧回调中分成2次进行加载。编译运行后,通过Trace图可以看到,动画首帧(232011)的耗时是12ms。相较于优化前的代码,不再是首帧占据大量的时间,而是将耗时分摊到了后面的动画帧中。在点击进入页面时,只放入了2条数据,所以232011帧的Measure[WaterFlow]只需要计算2个子组件的尺寸,并将剩余的4条数据放入productData,让后面的一帧(232013)计算剩余子组件的尺寸。当数据量更大时,可以将数据进行更多次拆分,将不会直接出现在屏幕上的数据放到第二帧或者第三帧中进行加载,降低首帧的响应时延,进而减少转场动画的卡顿现象。

image-20240717183924458

滑动场景

在日历应用中,需要在一个List里面加载每个月的全部天数,包括公历和农历日期,这样在一个Item中就会有最少58条数据加载,也就相当于需要58个组件。当列表滑动的时候,通过组件复用的aboutToReuse()接口设置新的数据,就会导致可能有58个组件一起刷新,可能会引起掉帧卡顿现象。

image-20240507183126622

解决思路

由于一次性加载大量数据、刷新大量组件会导致卡顿丢帧,那么减少一次性加载的数据量就是一种解决方法。但是由于业务需求,需要加载的数据总量和绘制的组件数量是不能减少的,那么只能想办法将数据进行拆分,将和数据相关的组件分成多次进行绘制。ArkTS中提供了DisplaySync(可变帧率),支持开发者设置回调监听,可以在回调里做一些数据的处理,在每一帧中加载少量的数据,减少卡顿或者滑动动画的掉帧现象。

优化示例

常规代码

通常情况下,会在aboutToReuse()中设置新的数据,并一次性绘制所有的组件。

@Entry
@Component
struct Direct {...// 初始化日历中一年的数据initCalenderData() {...}aboutToAppear() {...this.initCalenderData();}build() {Column() {...List() {LazyForEach(this.contentData, (monthItem: Month) => {// 每个月的日期ListItem() {ItemView({monthItem: monthItem,currentMonth: this.currentMonth,currentDay: this.currentDay})// 根据每月的天数设置复用ID,组件复用时会选择相同ID的组件进行复用.reuseId("reuse_id_" + monthItem.days.length.toString())}})}...}
}
@Reusable
@Component
struct ItemView {@State monthItem: Month = { month: '', num: 0, days: [], lunarDays: [] };...aboutToReuse(params: Record<string, Object>): void {hiTraceMeter.startTrace("reuse_" + (params.monthItem as Month).month, 1);this.monthItem = params.monthItem as Month;hiTraceMeter.finishTrace("reuse_" + (params.monthItem as Month).month, 1);}build() {Flex({ wrap: FlexWrap.Wrap }) {...// 日期信息ForEach(this.monthItem.days, (day: number, index: number) => {...}, (index: number): string => index.toString())}...}
}

在上面的代码中,通过组件复用,在ItemView的aboutToReuse()接口中,将一个月的数据直接设置到状态变量monthItem中,这样下面的Flex就会收到状态变量变更的消息通知,从而刷新组件中的数据。编译运行后,进入日历页面,通过SmartPerf Host工具,开始抓取Trace,然后滑动列表到最底端,结束Trace的抓取,通过SmartPerf Host对抓取的Trace文件进行分析,选中标签和相关数据区域,可以得到图1。图中三个Actual Timeline标签分别代表应用和RenderService每帧的总耗时、应用每帧的绘制时间和RenderService层每帧的绘制时间,render_service标签表示RenderService层每帧中的绘制操作,example.display标签是应用的bundlename,表示应用在每一帧中的操作,包括创建组件、加载数据等。

图1

image-20240507183017893

通过图中信息可以看到,滑动期间的帧率是113帧,按照手机120帧来计算,滑动期间掉帧率约为5.8%。放大图1后可以看到,应用每次加载新数据时(图2中橙色部分)RenderService层都会有一帧出现异常情况(图2中黄色部分)。此处对于图中颜色区域的解释,可参考SmartPerf Host工具。

图2

image-20240507183126622

将其中一部分继续放大后可以得到图3。选中Actual Timeline(render_service)标签中的146272后,可以通过箭头看到它所关联到的位置是Actual Timeline(example.display)标签中的209136和209137,即RenderService层出现的异常情况是由应用层中前面两帧里面的操作引起的。结合代码和箭头2的标签可以看到,在209135中调用了aboutToReuse接口,此时系统开始了组件复用的绘制操作。通过代码可以看到,在aboutToReuse接口将一个月的所有数据全部放入了当前被复用的组件中,并更新了所有的用于显示日期的Text组件中的数据(箭头3,diffIndexArray.lenght:35,表示有35个不同的元素),这就导致209136需要计算35个子组件的尺寸(箭头1),从而引起146272的绘制时间延长。在列表数据量较少时,其实并不会引起掉帧现象,因为每次延长帧的时间都很短,对帧率的影响较小,但是在列表数据较多时,就会因为延长帧过多,发生掉帧现象。

图3

image-20240507184557969

优化代码

通过DisplaySync中的帧回调方法,将数据拆分到每一帧中进行加载和绘制。此处只需要修改自定义子组件ItemView中加载数据的方式,所以与常规代码中相同的部分进行了省略。

首先,需要在ItemView中第一次使用时创建DisplaySync对象,设置期望帧率,添加帧回调的监听,然后进行启动。

@Reusable
@Component
struct ItemView {...aboutToAppear(): void {// 创建DisplaySync对象this.displaySync = displaySync.create();// 初始化期望帧率let range: ExpectedFrameRateRange = {expected: 120,min: 60,max: 120};// 设置期望帧率this.displaySync.setExpectedFrameRateRange(range);// 设置帧回调监听this.displaySync.on("frame", () => {...});// 开启监听帧回调this.displaySync.start();...  }...
}

然后,在监听中添加更新数据的代码。这里将每个月的数据更新拆分开来,第一步用来更新月份数据和计算总的执行步骤,最后一步将计数数据初始化,其余需要执行步骤的多少根据每次加载数据量会有所改变。

...
private temp: Month[] = [];
...
this.displaySync.on("frame", () => {// 数组中有数据时才开始执行if (this.temp.length > 0) {if (this.step === 0) {// 第一步:放入月份数据,并计算最多需要几帧完成数据操作hiTraceMeter.startTrace("reuse_" + this.step, 1);this.month = this.temp[0].month;this.monthNumber = this.temp[0].num;this.year = this.temp[0].year;this.maxStep = this.maxStep + Math.ceil(this.temp[0].days.length / this.MAX_EVERY_FRAME);hiTraceMeter.finishTrace("reuse_" + this.step, 1);this.step += 1;} else if (this.step === this.maxStep - 1) {// 最后一步:初始化部分计数数据this.temp.shift();this.step = 0;this.maxStep = 2;} else {hiTraceMeter.startTrace("reuse_" + this.step, 1);// 计算从所有数据中取值时的开始索引  let start: number = this.MAX_EVERY_FRAME * (this.step - 1);// 计算从所有数据中取值时的结束索引  let end: number = (this.MAX_EVERY_FRAME * this.step) > this.temp[0].days.length ? this.temp[0].days.length : this.MAX_EVERY_FRAME * this.step;// 更新日期数据  for (let i = start; i < end; i++) {this.days[i] = this.temp[0].days[i];this.lunarDays[i] = this.temp[0].lunarDays[i];}hiTraceMeter.finishTrace("reuse_" + this.step, 1);this.step += 1;}}
});
...

最后,在aboutToReuse接口中将数据放入数组中,用于帧回调中开始执行数据更新。

aboutToReuse(params: Record<string, Object>): void {hiTraceMeter.startTrace("reuse_" + (params.monthItem as Month).month, 1);this.temp.push(params.monthItem as Month);hiTraceMeter.finishTrace("reuse_" + (params.monthItem as Month).month, 1);
}

编译运行后,使用相同的方法,查看优化后的Trace信息,如图4所示。

图4

image-20240507190154885

从图4中可以看到,通过代码优化后,帧率是正常的120帧了。然后将图4中的Trace结果放大后可以看到图5,RenderService层出现的延长帧(Actual Timeline(render_service)标签中的黄色部分)明显减少了,已经不是优化前每次加载数据都会出现的情况了。

图5

image-20240507190741305

下面将图5中的信息继续放大一些,看一下现在每一帧里都做了什么操作,如图6所示。在211618中,开始调用aboutToReuse接口,由于只是将数据放入一个数组中,并没有更新复用组件中的数据,所以这一帧并没有发生延长现象。在211619中开始逐步更新复用组件中的数据,但是由于前一帧(211618)中并没有更新当前复用组件中的数据,所以在211619中并不需要绘制组件,所以此帧耗时依旧很短。结合代码可以看到,在211620中放入了5天的日期数据,由于前一帧(211619)只是设置了2条数据,并且只有1条会更新组件(this.month = this.temp[0].month会更新显示月份的Text),所以这一帧的绘制时间也不会超时。

图6

image-20240507195613210

继续看后面的Trace信息,如图7所示。和前一帧(211621)一样,此帧中更新了5天的日期数据,并且会重新测量上一帧(211621)中更新数据的5个Text组件尺寸(箭头1),而其余的组件由于数据并没有变动,所以测量被略过了(箭头2)。后面的帧是类似的,每次只会放入5天的数据,并且更新上一帧中设置的数据所关联的Text组件。由于每次更新的组件数量较少,每帧基本上都能在规定的时间内(1秒120帧,即8ms一帧)绘制完成,所以延长帧就会较少。这样不论列表中数据多还是少,都不会引起掉帧现象的发生。

图7

image-20240507200236522

不建议锁定最高帧率运行

不建议将ExpectedFrameRateRange中的expected、min、max都设置为120,否则会干扰系统的可变帧率机制运行,产生不必要的负载,进而影响到整机的性能和功耗。

反例

let sync = displaySync.create();
sync.setExpectedFrameRateRange({expected: 120,min: 120,max: 120
})

正例

let sync = displaySync.create();
sync.setExpectedFrameRateRange({expected: 60,min: 0,max: 60
})

主要原因有以下三点:

  1. ExpectedFrameRateRange中关键参数是expected(期望帧率),系统会优先按照expected设置的帧率执行。当系统难以满足expected帧率诉求时,会在min和max之间选一个更合适的帧率给到应用。
  2. 如果应用锁定120HZ,系统会优先满足应用的显式设置,按照120帧运行。此时手机功耗会显著增加,长时间运行会引起手机过热等严重影响用户体验的问题。同时也由于不必要的高帧率,会额外占据更多的算力,可能导致其他场景的响应受到不必要的延迟。
  3. 如果系统持续按照120HZ运行,从某种意义上来说此时系统的可变帧率能力已失效,这显然与可变帧率的设计原则不相符。

总结

通过上面的示例代码和优化过程,可以看到在列表中使用组件复用时,一次性全部加载时可能会引起掉帧。虽然在数据量较少时,单帧绘制的延长并不会引起掉帧,但是数据量变多后,这种延长帧的影响就会比较明显。合理进行数据拆分后,可以有效减少延长帧的发生,从而减少掉帧引起的性能问题。

FAQ

Q:在ItemView中,为什么要给ForEach设置第三个参数?

A:在组件复用中,如果有用到ForEach,必须设置第三个参数,即给每个数据设置一个key,否则ForEach中添加的组件不会被复用,而是会全部重新创建。

Q:对List中每一个ListItem的子组件都设置一个DisplaySync的帧回调监听,会不会引起性能问题?

A:并不会,通过示例中的Trace图可以看到,除了正在被复用的ItemView的DisplaySync的回调监听外,其余的监听耗时非常短,对性能的影响可以忽略不计。如图8所示。

图8

image-20240507194742115

Q:为什么抓取到的Trace中没有示例中那么多的标签?

A:需要通过hdc shell命令开启标签

hdc shell
param set persist.ace.debug.enabled 1
param set persist.ace.trace.enabled 1
param set persist.ace.trace.layout.enabled true
param set const.security.developermode.state true
param set persist.ace.trace.build.enabled 1

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:

GitCode - 全球开发者的开源社区,开源代码托管平台  希望这一份鸿蒙学习文档能够给大家带来帮助~


鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习教程+学习PDF文档

HarmonyOS Next 最新全套视频教程

  纯血版鸿蒙全套学习文档(面试、文档、全套视频等)       

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

相关文章:

HarmonyOS开发实战( Beta5版)高负载组件的渲染实践规范

简介 在应用开发中&#xff0c;有的页面需要在列表中加载大量的数据&#xff0c;就会导致组件数量较多或者嵌套层级较深&#xff0c;从而引起组件负载加重&#xff0c;绘制耗时增长。虽然可以通过组件复用避免组件重复创建&#xff0c;但是如果每个列表项中包含的组件较多&…...

NLP从零开始------16.文本中阶处理之序列到序列模型(1)

1. 序列到序列模型简介 序列到序列( sequence to sequence, seq2seq) 是指输入和输出各为一个序列(如一句话) 的任务。本节将输入序列称作源序列&#xff0c;输出序列称作目标序列。序列到序列有非常多的重要应用&#xff0c; 其中最有名的是机器翻译( machine translation), 机…...

【匈牙利汽车产业考察,开启新机遇】

匈牙利汽车工业发展历史悠久&#xff0c;拥有发达的基础设施和成熟的产业基础&#xff0c;全球20大汽车制造厂商中&#xff0c;有超过14家在匈牙利建立整车制造工厂和汽车零部件生产基地&#xff0c;比亚迪、宁德时代、欣旺达、蔚来等企业纷纷入驻。匈牙利位于东西方交汇处&…...

并行程序设计基础——动态进程管理

目录 一、组间通信域 二、动态创建新的MPI进程 1、MPI_COMM_SPAWN 2、MPI_COMM_GET_PARENT 3、MPI_COMM_SPAWN_MULTIPLE 三、独立进程间的通信 1、MPI_OPEN_PORT 2、MPI_COMM_ACCEPT 3、MPI_CLOSE_PORT 4、MPI_COMM_CONNECT 5、MPI_COMM_DISCONNECT 6、MPI_PUBLISH…...

C# 字符串(String)使用教程

在 C# 中&#xff0c;您可以使用字符数组来表示字符串&#xff0c;但是&#xff0c;更常见的做法是使用 string 关键字来声明一个字符串变量。string 关键字是 System.String 类的别名。 创建 String 对象 您可以使用以下方法之一来创建 string 对象&#xff1a; 通过给 Str…...

django之ForeignKey、OneToOneField 和 ManyToManyField

在Django中&#xff0c;ForeignKey、OneToOneField 和 ManyToManyField 是用于定义模型之间关系的字段类型。 ForeignKey ForeignKey 用于定义多对一的关系。例如&#xff0c;一个Employee可以属于一个Department&#xff0c;一个Department可以有多个Employee。 from djang…...

java.lang.IndexOutOfBoundsException: setSpan ( 0...x ) ends beyond length X

1&#xff0c;可能是EditText&#xff0c;setSelection(x)时超过了 输入框内容的实际长度导致的。 2&#xff0c;手机开启“拼写检查功能”&#xff0c;EditText设置了最大长度&#xff0c;选择提示的某一项文案时超过设置的最大长度限制&#xff0c;导致崩溃。 针对情况2 开…...

技术进展:CH-90树脂在去除硫酸钠柠檬酸钠溶液中铁锰离子上的应用

随着环境保护法规的日趋严格&#xff0c;以及工业生产中对产品纯度要求的不断提高&#xff0c;去除废水中的重金属离子已成为一个亟待解决的问题。铁和锰作为常见的杂质离子&#xff0c;在电池制造等行业中&#xff0c;对溶液纯度的影响不容忽视。 三元前驱体废水中通常含有硫…...

录屏时摄像头无法识别?如何录屏时打开摄像头,解决方案及录屏软件推荐

在数字时代&#xff0c;无论是游戏玩家、在线教育者还是企业培训师&#xff0c;录屏软件都已成为日常工作和娱乐中不可或缺的工具。但有时候想录制人物摄像头画面的时候&#xff0c;当录屏软件无法识别到摄像头时&#xff0c;这无疑会给用户带来不小的困扰。本文将提供一系列解…...

达梦数据库-DM8 企业版安装指南

一、DM8 企业版简介 达梦数据库(DM8)是中国自主研发的一款高性能数据库管理系统,广泛应用于企业级应用场景。DM8 企业版具备高可用性、强一致性和高性能等特点,支持多种操作系统和硬件平台。本文将详细介绍如何在 Kylin 操作系统上安装达梦数据库 DM8 企业版。 二、安装前…...

心脑血管科董田林医生:心律失常患者饮食,调养秘诀,助你找回健康心跳

在纷繁复杂的健康议题中&#xff0c;心律失常作为一种常见的心脏疾病&#xff0c;不仅影响着患者的生活质量&#xff0c;更牵动着每一个家庭的神经。幸运的是&#xff0c;通过科学合理的饮食调养&#xff0c;心律失常患者可以在很大程度上改善病情&#xff0c;逐步找回健康的心…...

期权杂记(一)

2024年9月5日&#xff1a; 切忌裸奔&#xff01;如果你想暴富&#xff0c;押注期权还不如去澳门&#xff1b;做任何策略都可以多多关注希腊字母&#xff1b;对冲也是又方向性的&#xff0c;可以偏购&#xff0c;也可以偏沽&#xff0c;通过Delta Money来尝试计算&#xff1b;单…...

【MATLAB源码-第163期】基于matlab的BPSK+瑞利(rayleigh)信道下有无波束成形误码率对比仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 在通信系统中&#xff0c;波束成形&#xff08;Beamforming&#xff09;技术是一种广泛使用的信号处理技术&#xff0c;通过调整天线阵列中各个元素的相位和幅度&#xff0c;使得信号在特定方向上增强&#xff0c;在其他方向…...

【数据分享】2000-2022年我国省市县三级的逐日O3数据(免费获取\excel\shp格式)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000-2022年的省市县三级的逐日PM2.5数据、2013-2022年的省市县三级的逐日CO数据、2013-2022年的省市县三级的逐日SO2数据、2008-2022年我国省市县三级的逐日NO2数据和2000-2022年我国省市县三级…...

Python 的http.server库详细介绍

http.server 是 Python 标准库中的一个模块&#xff0c;用于创建基本的 HTTP 服务器。这个模块非常适合用于开发、测试、以及在本地网络中共享文件。以下是对 http.server 模块的详细介绍。 Python 官方文档&#xff1a;http.server — HTTP 服务器 模块概述 http.server 提…...

使用ffmpeg在视频中绘制矩形区域

由于项目需要对视频中的人脸做定位跟踪&#xff0c; 我先使用了人脸识别算法&#xff0c;对视频中的每个帧识别人脸、通过人脸库比对&#xff0c;最终记录坐标等信息。 然后使用ffmpeg中的 drawbox 滤镜功能&#xff0c;选择性的绘制区域。从而实现人脸定位跟踪 1、drawbox …...

计算机,数学,AI在社会模拟中的应用

这些模型通常属于社会模拟的范畴&#xff0c;利用计算机技术和复杂系统理论来模拟和预测社会动态。以下是几种常见的社会模拟模型&#xff1a; 1. 系统动力学模型 系统动力学模型通过建立数学方程来描述社会系统中的各种变量及其相互关系。这种模型适用于宏观层面的社会变化分…...

【数据结构】排序算法系列——插入排序(附源码+图解)

插入排序 算法思想 插入排序的算法思想其实很容易理解&#xff0c;它秉持着一个不变的循环&#xff1a;比较->交换->比较->交换…因为我们排序最终的目的是要得到递增或者递减的数据&#xff0c;那么在原有的数据中&#xff0c;我们可以将数据依次两两进行比较&…...

TOMATO靶机漏洞复现

步骤一&#xff0c;我们来到tomato页面 什么也弄不了只有一番茄图片 弱口令不行&#xff0c;xxs也不行&#xff0c;xxe还是不行 我们来使用kali来操作... 步骤二&#xff0c;使用dirb再扫一下, dirb http://172.16.1.133 1.发现这个文件可以访问.我们来访问一下 /antibot_i…...

高基数 GroupBy 在 SLS SQL 中的查询加速

作者&#xff1a;顾汉杰&#xff08;执少&#xff09; 什么是高基数 GroupBy 简单来说&#xff0c;想要分析的数据&#xff0c;拥有超多的“唯一值计数”&#xff08;Distinct Count&#xff09;&#xff0c;而我们需要对这些数据进行分组分析&#xff08;如统计次数、排名、…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...