HarmonyOS--提升动画运行流畅度

2024-08-29 08:36

本文主要是介绍HarmonyOS--提升动画运行流畅度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、概述

        动画在应用开发中扮演着重要的角色,能够提升用户体验,传达信息,引导用户操作,提升应用品质和增加视觉吸引力。而动画的性能表现也至关重要,优化可以从属性更新和布局等几个方面考虑,尽可能减少冗余刷新。本文将介绍如下4种优化手段,通过这些优化手段的单个使用或组合使用,可以对动画帧率、应用卡顿等方面带来优化,提升性能和用户体验:

  • 使用系统提供的动画接口:系统接口经过精心设计和优化,能够在不同设备上提供流畅的动画效果,最大程度地减少丢帧率和卡顿现象。
  • 使用图形变换属性变化组件布局:通过对组件的图形变换属性进行调整,而不是直接修改组件的布局属性,可以减少不必要的布局计算和重绘操作,从而降低丢帧率,提升动画的流畅度和响应速度。
  • 参数相同时使用同一个animateTo:当多个动画的参数相同时,合并它们并使用同一个animateTo方法进行处理能够有效减少不必要的计算和渲染开销。
  • 多次animateTo时统一更新状态变量:在进行多次动画操作时,统一更新状态变量可以避免不必要的状态更新和重复渲染,从而减少性能开销。

        测试的关键指标: 

  • 丢帧率(Janky Frames):表示一个时间周期内的丢帧比率,指一个时间周期内有问题的帧比例。HarmonyOS系统要求每一帧都要在11.1ms(90Hz刷新率)内绘制完成,如果页面没有在11.1ms内完成这一帧的绘制,就会出现丢帧。部分丢帧一般用户肉眼是感知不到的,只有出现连续丢帧用户才有明显感知。
  • 最大连续丢帧数(maximum successive frame dropping count):表示从页面开始有响应变化到页面结束刷新的过程中,由于显示器画面刷新频率低于预设的画面帧率而未能正常呈现的最大连续帧数。一般而言,当连续值超过3时,用户可以明显感知到卡顿掉帧,数值越大卡顿时间越长。
  • 平均1s大卡顿次数:表示程序运行过程中平均1s出现连续丢3帧以上的卡顿的次数,数值越大用户体验感越差。

二、使用系统提供的动画接口

         实现效果:同一界面多个按钮同时缩放的场景

        

 

         1、自定义动画

        自定义动画是指通过编写自定义的动画逻辑和计算过程来实现特定的动画效果。开发人员可以根据应用的需求和设计要求,使用自定义的动画算法和逻辑来创建独特的动画效果。自定义动画的优势在于可以实现非常个性化的动画效果,并且能够完全控制动画的每一个细节,但需要开发人员具备一定的动画算法和计算能力。

// 自定义动画代码
@Entry
@Component
struct CustomAnimationExample {@State widthSize: number = 80@State heightSize: number = 40@State flag: boolean = true// 自定义动画函数computeSize() {let duration = 2000// 设定循环周期let period = 1let widthSizeEnd = 0let heightSizeEnd = 0if (this.flag) {widthSizeEnd = 50heightSizeEnd = 25} else {widthSizeEnd = 80heightSizeEnd = 40}// 计算循环次数let doTimes = duration / period// 计算每次循环变化量let deltaHeight = (heightSizeEnd - this.heightSize) / doTimeslet deltaWeight = (widthSizeEnd - this.widthSize) / doTimes// 循环,组件每个周期增加一部分宽高for (let i = 1; i <= doTimes; i++) {let t = period * (i);setTimeout(() => {this.heightSize = this.heightSize + deltaHeightthis.widthSize = this.widthSize + deltaWeight}, t)}this.flag = !this.flag}build() {Column() {Button().width(this.widthSize).height(this.heightSize)// 其他相似布局…Button('click me').onClick(() => {let delay = 500// 调用自定义函数setTimeout(() => {this.computeSize()}, delay)}).width('50%').height('15%').backgroundColor(0x317aff)}.width('100%').margin({ top: 5 })}
}

        2、系统动画接口

        系统动画接口是指通过使用系统提供的动画框架和接口来实现动画效果。在移动应用开发中,通常会使用属性动画来实现各种动画效果。通过可动画属性改变引起UI上产生的连续视觉效果,即为属性动画。属性动画是最基础易懂的动画,ArkUI提供两种属性动画接口animateTo和animation驱动组件属性按照动画曲线等动画参数进行连续的变化,产生属性动画。使用系统提供的动画接口可以简化动画的实现过程,并且能够充分利用系统优化的动画计算和渲染能力,从而提高动画的性能和流畅度。

// 系统动画
@Entry
@Component
struct PropertyAnimateToExample {@State widthSize: number = 80;@State heightSize: number = 40;@State flag: boolean = true;build() {Column() {Button().width(this.widthSize).height(this.heightSize)// 对Button组件的宽高属性进行动画配置.animation({duration: 2000, // 动画时长curve: Curve.Linear, // 动画曲线delay: 500, // 动画延迟iterations: 1, // 播放次数playMode: PlayMode.Normal // 动画模式}) // 其他相似布局…Button('click me').onClick((event?: ClickEvent | undefined) => {if (this.flag) {this.widthSize = 50this.heightSize = 25} else {this.widthSize = 80this.heightSize = 40}this.flag = !this.flag}).width('50%').height('15%').backgroundColor(0x317aff)}.width('100%').margin({ top: 5 })}
}// 或
@Entry
@Component
struct ExplicitAnimateToExample {@State widthSize: number = 80;@State heightSize: number = 40;@State flag: boolean = true;build() {Column() {Button().width(this.widthSize).height(this.heightSize)// 其他相似布局…Button('click me').onClick((event?: ClickEvent | undefined) => {// 对Button组件的宽高属性进行动画配置if (this.flag) {animateTo({duration: 2000, // 动画时长curve: Curve.Linear, // 动画曲线delay: 500, // 动画延迟iterations: 1, // 播放次数playMode: PlayMode.Normal // 动画模式}, () => {this.widthSize = 50;this.heightSize = 25;})} else {animateTo({duration: 2000, // 动画时长curve: Curve.Linear, // 动画曲线delay: 500, // 动画延迟iterations: 1, // 播放次数playMode: PlayMode.Normal // 动画模式}, () => {this.widthSize = 80;this.heightSize = 40;})}this.flag = !this.flag;}).width('50%').height('15%').backgroundColor(0x317aff)}.width('100%').margin({ top: 5 })}
}

        3、结论

        系统动画接口内部实现对开发者是透明的,已经进行了尽可能的性能优化,避免开发者使用自定义动画时产生不必要的性能劣化。自定义动画适合实现个性化的、复杂的动画效果,而系统提供的动画接口则适合实现常见的动画效果并且能够获得更好的性能表现。因此,在动画能够使用系统接口实现的情况下,开发者应尽量使用系统接口实现,保持动画的流畅性和稳定性,提升应用的性能表现。 

三、使用图形变换属性变化组件

        实现效果:针对同一界面多个图片同时缩放并位移的场景(如下图所示),分别通过改变布局属性、改变图形变换属性实现

         

        1、改动布局属性

·常见的布局属性包括位置、大小、内边距、外边距、对齐方式、权重等。当这些布局属性发生改变时,界面将重新布局以适应新的属性值。

@Entry
@Component
struct Index {@State imageWidth: number = 60;@State imageHeight: number = 60;@State xPosition: number = 0;@State yPosition: number = 0;build() {Column(){Image($r('app.media.sample')).width(this.imageWidth).height(this.imageHeight).position({x: this.xPosition, y: this.yPosition})// 其他相似布局…Button("布局属性")// 按键属性设置….onClick(() => {let doTimes = 10;// 按播放次数循环播放动画for (let i = 0; i < doTimes; i++) {// 间隔播放位置、宽高变化if (i % 2 == 0){setTimeout(() => {animateTo({ duration:1000 }, () => {this.imageWidth = 120;this.imageHeight = 120;this.xPosition = 15;this.yPosition = 15;})}, 1000 * i)} else {setTimeout(() => {animateTo({ duration: 1000 }, () => {this.imageWidth = 60;this.imageHeight = 60;this.xPosition = 0;this.yPosition = 0;})}, 1000 * i)}}})}.width('100%').margin({ top: 5 })}
}

        2、改动图形变换属性

·图形变换属性是指对组件布局结果的变换操作,如平移、旋转、缩放等操作。通过改变这些图形变换属性,可以实现对组件布局完成后,在界面上的位置和形态进行动态变换。

@Entry
@Component
struct Index {@State imageScaleX: number = 1;@State imageScaleY: number = 1;@State imageTranslateX: number = 0;@State imageTranslateY: number = 0;build() {Column() {Image($r('app.media.like')).scale({ x: this.imageScaleX, y: this.imageScaleY, centerX: 0, centerY: 0 }).translate({ x: this.imageTranslateX, y: this.imageTranslateY })// 其他相似布局…Button("图形变换属性")// 按键属性设置….onClick(() => {let doTimes = 10;// 按播放次数循环播放动画for (let i = 0; i < doTimes; i++) {if (i % 2 == 0) {setTimeout(() => {animateTo({ duration: 1000 }, () => {this.imageScaleX = 2;this.imageScaleY = 2;this.imageTranslateX = 15;this.imageTranslateY = 15;})}, 1000 * i)} else {setTimeout(() => {animateTo({ duration: 1000 }, () => {this.imageScaleX = 1;this.imageScaleY = 1;this.imageTranslateX = 0;this.imageTranslateY = 0;})}, 1000 * i)}}})}.width('100%').margin({ top: 5 })}
}

图形变换属性

布局属性

rotate

/

translate

position、offset

scale

width、height、Size

transform

/

        3、结论 

         使用图形变化属性改变图片大小和位置时,能够显著降低丢帧率和大卡顿的发生频率。界面布局是非常耗时的操作,因此频繁地改动布局属性会导致界面性能下降,出现卡顿现象,影响用户体验。因此,在动画能够使用图形变化属性实现的情况下,开发者应尽量使用图形变化属性实现,保持动画的流畅性和稳定性,提升应用的性能表现。

四、参数相同时使用同一个animateTo

        每次调用animateTo方法,都会触发一次属性变化,这意味着在每次动画执行时都需要进行动画前后的对比,以确定属性的变化情况。当多次连续调用animateTo时,会增加额外的布局计算和绘制开销,从而降低性能表现。特别是当这些animateTo操作针对同一个组件的属性时,会导致该组件更新的次数增加,进一步影响性能。

        在实际开发中,如果多个属性需要以相同的动画参数进行变化,推荐将它们放到同一个动画闭包中执行。通过将多个属性的动画操作合并到同一个动画闭包中,可以减少对组件的多次更新,避免重复的布局计算和绘制操作,提升动画效果的性能。

        除了性能方面的优势,将多个属性的动画操作合并到同一个动画闭包中还有助于提高代码的可读性和维护性。通过集中管理相关联的属性变化,可以使代码结构更加清晰,便于后续的维护和修改。

        实现效果:针对多个相同组件同时修改多个属性的场景

        

        1、多个animateTo闭包

@Entry
@Component
struct MyComponent {@State w:number = 150@State h:number = 2@State brightNum:number = 1.5@State color:Color = Color.Red// 动画闭包1,设置宽度变化func1() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.w = (this.w === 80 ? 150 : 80);});}// 动画闭包2,设置颜色变化func2() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);});}// 动画闭包3,设置高度变化func3() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.h = (this.h === 2 ? 5 : 2);});}// 动画闭包4,设置高光变化func4() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.brightNum= (this.brightNum=== 1.5 ? 1 : 1.5);});}build() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h).brightness(this.brightNum)// 其他相似布局…Button("click nFunc")// 按键属性设置….onClick(() => {let doTimes = 10;// 按播放次数循环播放动画for (let i = 0; i < doTimes; i++) {setTimeout(() => {this.func1();this.func2();this.func3();this.func4();}, 1000 * i)}})}}
}

        2、一个animateTo闭包

@Entry
@Component
struct MyComponent {@State w:number = 150@State h:number = 2@State brightNum:number = 1.5@State color:Color = Color.Red// 统一动画闭包,同时设置四个属性变化func() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.w = (this.w === 80 ? 150 : 80);this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);this.h = (this.h === 2 ? 5 : 2);this.brightNum= (this.brightNum=== 1.5 ? 1 : 1.5);});}build() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h).brightness(this.brightNum)// 其他相似布局…Button("click oneFunc")// 按键属性设置….onClick(() => {let doTimes = 10;// 按播放次数循环播放动画for (let i = 0; i < doTimes; i++) {setTimeout(() => {this.func();}, 1000 * i)}})}}
}

        3、结论

        将多个属性变化动画合并到同一个animateTo动画闭包中能够显著降低丢帧率和大卡顿的发生频率。合并动画操作可以减少不必要的布局计算和绘制开销,从而提升动画的流畅性和性能表现,有助于优化动画效果的性能。

五、多次animateTo时统一更新状态变量

        使用animateTo方法执行动画时,会对执行动画闭包前后的状态进行对比,然后只对差异部分进行动画处理。

        在动画执行过程中,脏节点是指在界面上需要进行重新绘制的区域。如果状态发生了变化,ArkUI会跟踪这些变化,并在动画闭包执行前进行状态对比,相关的脏节点会被标记为需要刷新,以便在动画闭包执行前对这些脏节点进行重新绘制。这样,只有发生变化的部分才会被纳入动画处理,而不需要重新绘制整个界面。

        这种差异对比的方式能够显著减少不必要的绘制操作,提高动画的性能和流畅度。

         实现效果:针对多个相同组件同时修改多个属性的场景

        

 

        1、在多个animateTo之间更新状态变量

         

@Entry
@Component
struct MyComponent {@State w: number = 100@State h: number = 2@State color: Color = Color.Redbuild() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h)// 其他相似布局…Button("click1")// 按键属性设置….onClick(() => {let doTimes = 5;for (let i = 0; i < doTimes; i++) {setTimeout(() => {this.w = 80// h是非动画属性this.h = 4animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {this.w = (this.w === 80 ? 150 : 80);});// 在两个animateTo之间更新状态变量this.color = Color.YellowanimateTo({ curve: Curve.Linear, duration: 2000 }, () => {this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);});}, 2000 * i)}})}}
}

        2、在animateTo之前显式指定属性初值

         

@Entry
@Component
struct MyComponent {@State w: number = 100@State h: number = 2@State color: Color = Color.Redbuild() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h)// 其他相似布局…Button("click2")// 按键属性设置….onClick(() => {let doTimes = 5;for (let i = 0; i < doTimes; i++) {setTimeout(() => {// 在动画之前显式的指定所有需要动画的属性初值this.w = 80this.color = Color.Yellow// 动画1,修改宽度animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {this.w = (this.w === 80 ? 150 : 80);});// 动画2,修改颜色animateTo({ curve: Curve.Linear, duration: 2000 }, () => {this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);});// 动画完成后刷新非动画属性this.h = 5}, 2000 * i)}})}}
}

        3、在animateTo之前使用原始状态

        

@Entry
@Component
struct MyComponent {//原始状态@State w: number = 80@State h: number = 5@State color: Color = Color.Yellowbuild() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h)// 其他相似布局…Button("click3")// 按键属性设置….onClick(() => {let doTimes = 5;for (let i = 0; i < doTimes; i++) {// 直接使用原始状态实现动画setTimeout(() => {animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {this.w = (this.w === 80 ? 150 : 80);});animateTo({ curve: Curve.Linear, duration: 2000 }, () => {this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);});}, 2000 * i)}})}}
}

        4、结论

        在进行属性变化动画时,显式指定属性初值或者使用原始状态作为动画的起始状态能够显著降低丢帧率和大卡顿的发生频率。因此,在进行动画操作时,合理管理状态变量的更新和初始值设定对于优化动画效果的性能至关重要,开发者应根据需要,尽可能地避免在多个animateTo之间更新状态变量,从而提升动画的流畅性和性能表现。

六、使用renderGroup

        renderGroup是组件通用方法,它代表了渲染绘制的一个组合。其核心功能就是标记组件,在绘制阶段将组件和其子组件的绘制结果进行合并并缓存,以达到复用的效果,从而降低绘制负载。

        组件渲染流程图如下所示:

        

        在进行缓存更新时,需要满足以下三个条件:

  • 组件在当前组件树上。
  • 组件renderGroup被标记为true。
  • 组件内容被标脏。

        在进行缓存清理时,需要满足以下任意条件:

  • 组件不存在于组件树上。
  • 组件renderGroup被标记为false。

具体缓存管理流程图如下所示:

         

        实现效果: 在同一个页面下使用了固定的图片和文本内容,并且每个组件统一使用旋转和缩放的动效

        

// IconItem.ets
@Component
export  struct IconItem {@StorageLink('renderGroupFlag') renderGroupFlag: boolean = false;image: string | Resource = '';text: string | Resource = '';build() {Flex({direction: FlexDirection.Column,justifyContent: FlexAlign.Center,alignContent: FlexAlign.Center}) {Image(this.image).height(20).width(20).objectFit(ImageFit.Contain).margin({ left: 15 })Text(this.text).fontSize(10).fontColor("#182431").margin({ top: 5 }).width(50).opacity(0.8).textAlign(TextAlign.Center)}.backgroundColor('#e3e3e3').width(50).height(50).borderRadius(25)// 在IconItem内调用renderGroup,false为关闭,true为开启.renderGroup(this.renderGroupFlag)}
}// Index.ets
import { IconItem } from './IconItem'// IconItem相关数据
class IconItemSource {image: string | Resource = ''text: string | Resource = ''constructor(image: string | Resource = '', text: string | Resource = '') {this.image = image;this.text = text;}
}@Entry
@Component
struct Index {// renderGroup接口是否开启@State renderGroupFlag: boolean = false;private iconItemSourceList: IconItemSource[] = [];aboutToAppear() {// 遍历添加60个IconItem的数据for (let index = 0; index < 20; index++) {const numStart: number = index * 3;// 此处循环使用三张图片资源this.iconItemSourceList.push(new IconItemSource($r('app.media.album'), `item${numStart + 1}`),new IconItemSource($r('app.media.applet'), `item${numStart + 2}`),new IconItemSource($r('app.media.cards'), `item${numStart + 3}`),);}}build() {Column() {Row() {Row() {Text('场景示例').fontSize(24).lineHeight(24).fontColor(Color.Black).fontWeight(FontWeight.Bold).margin({ left: 30 })}// 动态切换renderGroup功能Stack({ alignContent: Alignment.End }) {Button(this.renderGroupFlag ? 'renderGroup已开启' : 'renderGroup已关闭', {type: ButtonType.Normal,stateEffect: true}).fontSize(12).borderRadius(8).backgroundColor(0x317aff).width(150).height(30).margin({ right: 30 }).onClick(() => {this.renderGroupFlag = !this.renderGroupFlag;AppStorage.setOrCreate('renderGroupFlag', this.renderGroupFlag)})}}.height(56).width('100%').backgroundColor(Color.White).justifyContent(FlexAlign.SpaceBetween)// IconItem放置在grid内GridRow({columns: 6,gutter: { x: 0, y: 0 },breakpoints: { value: ["400vp", "600vp", "800vp"],reference: BreakpointsReference.WindowSize },direction: GridRowDirection.Row}) {ForEach(this.iconItemSourceList, (item: IconItemSource) => {GridCol() {IconItem({ image: item.image, text: item.text }).transition(TransitionEffect.scale({ x: 0.5, y: 0.5 }).animation({duration: 3000, curve: Curve.FastOutSlowIn, iterations: -1 }).combine(TransitionEffect.rotate({ z: 1, angle: 360 }).animation({ duration: 3000, curve: Curve.Linear, iterations: -1 })))}.height(70).width('25%')})}.width("100%").height("100%")}.width('100%').height('100%').alignItems(HorizontalAlign.Center)}
}

        1、结论

        在在单一页面上存在大量应用动效的组件,且组件和其子组件各属性保持固定、组件统一应用动效时,开发者可以使用renderGroup来提升应用的性能,保证应用及动画的流畅性。

七、总结

  • 用户体验:流畅的动画能够提升用户体验,使用户感到界面更加生动和直观。相反,卡顿或者延迟的动画会带来不良的体验。
  • 设备资源消耗:动画的性能表现会直接影响设备的资源消耗,尤其是CPU和GPU的占用。低性能的动画可能导致设备发热、耗电量增加,甚至影响其他应用的运行。
  • 应用稳定性:性能较差的动画可能会导致应用崩溃或者卡顿,影响应用的稳定性和可靠性。
  • 节省流量:高性能的动画可以减少数据传输量,对于移动应用来说,这意味着可以节省用户的流量消耗。
  • 适应不同设备:不同设备的性能差异很大,提升动画性能可以使应用在各种设备上都能够流畅运行。

 

这篇关于HarmonyOS--提升动画运行流畅度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1117373

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

跨系统环境下LabVIEW程序稳定运行

在LabVIEW开发中,不同电脑的配置和操作系统(如Win11与Win7)可能对程序的稳定运行产生影响。为了确保程序在不同平台上都能正常且稳定运行,需要从兼容性、驱动、以及性能优化等多个方面入手。本文将详细介绍如何在不同系统环境下,使LabVIEW开发的程序保持稳定运行的有效策略。 LabVIEW版本兼容性 LabVIEW各版本对不同操作系统的支持存在差异。因此,在开发程序时,尽量使用

java学习,进阶,提升

http://how2j.cn/k/hutool/hutool-brief/1930.html?p=73689

如何在运行时修改serialVersionUID

优质博文:IT-BLOG-CN 问题 我正在使用第三方库连接到外部系统,一切运行正常,但突然出现序列化错误 java.io.InvalidClassException: com.essbase.api.base.EssException; local class incompatible: stream classdesc serialVersionUID = 90314637791991

用Unity2D制作一个人物,实现移动、跳起、人物静止和动起来时的动画:中(人物移动、跳起、静止动作)

上回我们学到创建一个地形和一个人物,今天我们实现一下人物实现移动和跳起,依次点击,我们准备创建一个C#文件 创建好我们点击进去,就会跳转到我们的Vision Studio,然后输入这些代码 using UnityEngine;public class Move : MonoBehaviour // 定义一个名为Move的类,继承自MonoBehaviour{private Rigidbo

JAVA用最简单的方法来构建一个高可用的服务端,提升系统可用性

一、什么是提升系统的高可用性 JAVA服务端,顾名思义就是23体验网为用户提供服务的。停工时间,就是不能向用户提供服务的时间。高可用,就是系统具有高度可用性,尽量减少停工时间。如何用最简单的方法来搭建一个高效率可用的服务端JAVA呢? 停工的原因一般有: 服务器故障。例如服务器宕机,服务器网络出现问题,机房或者机架出现问题等;访问量急剧上升,导致服务器压力过大导致访问量急剧上升的原因;时间和