鸿蒙-应用系统优化

文章目录:

在开发Harmonyos应用时,优化应用性能是至关重要的。通过ArKTS高性能编程、减少丢帧卡顿、提升应用启动和响应速度
可以有效提升用户体验。本文将介绍一些优化Harmonyos应用性能的方法,以及常用的性能调优工具。

ArkTs高性能编程

ArkTS高性能编程规则

更有利于方舟编译运行时进行编译优化,生成更高性能的机器码,保障程序运行得更快。

使用AOT模式对应用进行编译优化

配置文件引导型优化)方式,提前生成高性能方舟编译运行时通过采用PGO(Profile-Guided-0ptimization,机器码,从而提升程序运行速度。

ArkTS是基于TypeScript设计的,但出于代码的稳定性和性能考虑,一些TypeScript的特性被限制了。比如需要不支持属性的动态变更、变量或参数需要有明确的类型声明和返回值声明等。

// 错误用法
//case1
let p1 = new Point(1.0, 1.0)
delete p1.x//Compile-time eror
//case2
let p2 = new Point(2.0, 2.0)
p2.z="Labe!” //Compile-time erar
//case3
.let p3 = new Point(3.0,3.0)
let unigue =Svmbol()//Compile-time error
p3[uniquel=p3.x //Compile-time error
//caseA
let p4 = new Point(4.0, 4.0)
p4.x="Hello!” //campile-time error
// 正确用法
function distance(p1 : Point, p2 : Point) : number {
return Math,sqrt((p2.x-p1.x)“(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y))
let p5 = new Point(5.0, 5.0)
let p6 = new Point(6.0.6.0)
console,log("Distance between p5 and p6:" + distance(p5, p6))
class Point {
public x : number
public y:number
constructor(x:numiber, y:number){
this.x = x
this.y= y

严格遵循ArkTS的编码规则,禁用 @ts-ignore @ts-expect-error等屏蔽编译校验的命令,这些命令规避了系
统的编译校验,容易引起稳定性问题。

// @ts-ignore
import backgroundTaskManager from'@ohos.backgroundTaskManager'.

开启TypeScript的严格模式,比如需要严格判空、严格函数类型检查、严格成员初始化等,提高代码的质
量和可维护性,避免一些常见错误,

public alarm (): boolean | number | undefined {
return(
(this. opts.below && this. value < this. opts.below)||(this. opts.over && this. value >this. opts.over)

arkTS设计规则时主要考虑,语言的静态性,尽量在运行时保留属性,类,成员变量等具体类型。提高代码可维护性,并且运行时可以做AOT优化。编译性能优化等。

ArkTS不支持any和unknown

不支持使用any和unknown

请使用明确类型,或者使用联合类型、泛型或老object替代any,必须使用any的场景可以使用ESObject代替,但是可能存在性能问题,谨慎使用。

使用AOT模式

AOT(Ahead ofTime)即提前编译,能够在 Host端将字节码提前编译成 Target端可运行的机器码,这样字节码可以获得充分编译优化,放到 Target 端运行时可以获得加速。

未启用AOT

启用AOT

提升应用启动和响应速度

应用冷启动过程

应用启动时,后台无该应用的进程,需要创建新的进程,这种启动方式叫做冷启动,冷启动的过程大致可分成下面四个阶段:应用进程创建和初始化、App和Ability的初始化、Ability生命周期、加载绘制首页。

缩短应用冷启动的方法

1、缩短应用进程创建&初始化阶段耗时:设置合适分辨率的startwindowIcon
2、缩短Application&Ability初始化阶段耗时:减少首页Ability或者Page中import的模块
3、缩短Ability生命周期阶段耗时:使用异步加载
4、缩短加载绘制首页阶段耗时:延迟加载

使用异步加载

生命周期中如果必须要运行耗时操作,可以考虑使用异步接口或者异步加载的方式,延迟耗时操作的运行时机,提升应用的启动速度。

// 优化前
@Entry
@Component
struct TaskSync {
@State private text: string = undefined,
private count: number = undefined;
aboutToAppear(){
this.text =“hello world”
this.computeTask();//同步任务
// 优化后
@Entry
@Companent
struct TaskAsync {
@State private text: string = undefined;
private count: number =undefined;
aboutToAppear(){
this.text = “hello world"
this.computeTaskAsync();//异步任务

使用动态加载

页面在引入依赖的加载模块时,如果不是首帧必须显示的内容模块,可以使用动态加载的方式(await import)延迟模块的加载时间,加快首帧显示的加载速度。

// 优化前
import l pageOne, pagesOneData } fram'./pageOne"
import { pageTwo, pagesTwoData } from './pageTwo
import i pageThree, pagesThreeData f from './pageThree'
import { pageFour, pagesFourData jfrom'./pageFour
...
import router from '@ohos.router
// 优化后
async loadPageOne(key: string){
if (key == “pageOne”){
let { PageOneLoader }= await import("../pages/PageLoader")
this.PageOneLoader = PageOneloader;

延迟加载

在长列表场景中,我们推荐大家使用LazyForEach替代ForEach。
LazyForEach是一种懒加载的实现模式,能够显著提升列表页面的加载速度。

// 不推荐
ForEach(
arr. anyl],// 需要进行数据迭代的列表数组
itemGenerator:(item:any, index?:number)=>void, //子组件生成函数
keyGenerator?:(item:any,index?:number)=>string //(可选)键值生成函数
// 推荐
LazyForEach(
dataSource:lDataSource,    // 需要进行数据迭代的列表数组
itemGenerator:(item: any) => void, //子组件生成函数
keyGenerator?:(item: any)=> string //(可选)键值生成函数

使用缓存

当使用LazyForEach时,我们可以搭配使用cachedCount方法,自定义控制列表的缓存数量,实现更优的滑动体验。

build()(
Column(){
List() {
...
LazyForEach(this.chatList, (msg: ChatModel) =>(
Listttem()(
ChatView({ chatitem: msg ))
},(msg: ChatModel) => msg,user.userld)
backgroundCoioriColor.White)
listDirectionlAxis.Vertical)
...
// 缓存列表数量
.cachedCount(this.list cachedCount ? Constants.CACHED COUNT . 0)

减少丢帧卡顿

避免在主线程上执行耗时操作

UI主线程是HarmonyOS应用中最重要的线程之一,在主线程上执行耗时操作会阻塞U渲染,从而导致U1主线程的负载过高。因此,可以将耗时操作放在TaskPool或者Worker等后台线程中执行。

减少渲染进程的冗余开销

使用资源图代替绘制、合理使用renderGroup、尺寸位置设置尽量使用整数,可以减少渲染所需的时间,从而减少丢帧卡顿。

减少视图嵌套层级

应用开发中的用户界面(UI)布局是用户与应用程序交互的关键部分。不合理的布局越多,视图的创建、布局、渲染等流程所需的时间就越长。因此,减少嵌套层次或者使用高性能布局节点,可以减少丢帧卡顿。

组件复用配合LazyForEach

使用ArKUI开发范式提供的组件复用机制,通过重复利用已经创建过并缓存的组件对象,降低组件短时间内频繁创建和销毁的开销,提升组件加载效率,降低UI线程负载,从而减少丢帧卡顿。

精确控制状态变量的关联组件数

@state等状态变量关联组件越多,状态数据变更时刷新的组件越多,U线程负载越重,因此移除兄余的组件关联可以减少丢帧卡顿。

在对象上谨慎使用状态变量关联

避免在主线程上执行耗时操作

U主线程是应用中最重要的线程之一,在主线程上执行耗时操作会阻塞U渲染,从而导致U主线程的负载过高。因此,可以将耗时操作(视频解压场景、朋友圈网络数据处理场景)放在TaskPool或者Worker等后台线程中执行。可以从上图的几组数据中看出,把耗时操作放在子线程可以显著降低丢帧率,提升应用的流畅度。

减少渲染进程的几余开销-动画场景示例

组件转场动画推荐使用transition
组件转场动画指在组件出现和消失时做动画,有两种实现方式
1、属性动画+动画结束回调逻辑处理,即animateTo(不推荐)。

2、组件转场动画transition (推荐) 。

// 不推荐
Text("toggle state")
.onClick(()=> {
this.count++;
const thisCount: number = this.count;
this.show = true
// 通过改变透明度属性,对Text控件做隐藏或出现的动画
animateTo({duration:1000,onFinish:()=>{
//最后一个动画且是让Text控件隐藏的动画,再改变条件使控件消失
if(thisCount === this,count && this.mOpacity === 0){
this.show = false
}h () =>{
this.mOpacity=this.mOpacity === 1? 0 :1;
// 推荐
build(){
Column()[
Row(){
if (this.show) {
Text('value')
//设置id,使转场可打断
.id("'myText")
.transition(TransitionEffect.OPAclTY,animation({duration: 1000}))
.width("100%")
.height(100)
.justifyContent(FlexAlign.Center)
Text("toggle state")
.onClick(()=> {
//通过transition,做透明度的出现或消失动画
this.show = !this.show

合理使用动效

动画参数相同时使用同一个animateTo

由于每次animateTo都需要进行动画前后的对比,使用多个animateTo的性能就不如只使用一个animateTo。特别是针对设置在同一个组件的属性,能减少该组件更新的次数。

func1(){
animateTo({curve:Curve.Sharp,duration:1000},()=>{
this.w=(this.w === 100 ? 200:100);

func2(){
animateTo({curve:Curve.Sharp, duration: 1000},()=>{
this.color =(this.color === ColorYellow ? Color.Red : ColorYellow);

func(){
animateTo({curve:Curve.Sharp, duration:1000},()=>{
this.w=(this.w === 100 ? 200 :100);
this.color=(this.color === ColorYellow ? Color.Red : ColorYellow);

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

animateTo会将执行动画闭包前后的状态进行对比,对差异部分进行动画。为了对比,会在执行animateTo的动画闭包之前,将所有变更的状态变量和脏节点都刷新。如果多个animateTo之间存在状态更新,会导致执行下一个animateTo之前又存在需要更新的脏节点,可能造成几余更新。

struct MyComponent {
@State width: number=200
@State height; number = 50
@State color: Color = Color.Red
build() {
Column() {
Text(“click”)
.height(this.h)
.onClick(() =>.{
this.w=100
//h是非动画属性
this.b=100
animateTo({curve: Curve.Sharp,duration: 1000},()=>{
this.w =200
});
this.color= Color.Yellow
animateTo(lcurve Curve.linear, duration: 2000},()=>{
this.color = Calor.Red
});
.width("100%")
height("100%"

合理使用RenderGroup

首次绘制组件时,若组件被标记为启用renderGroup状态,将对组件以及其子节点进行离屏绘制,将绘制结果进行缓存,此后当需要重新绘制组件时,就
会优先使用缓存而不必重新执行绘制了,从而降低绘制负载,优化渲染性能。

build() {
Flex({
direction: FlexDirection.Column
justifyContent: FlexAlign.Center
alignContent: FlexAlign.Center
H
Image(this.image)
.height(20)
.width(20)
.objectFit(lmageFit.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)
renderGroup(true)//使用RenderGroup

前提:没有绑定任何的状态变量。即build的内容不变。

减少视图嵌套层级

嵌套层级越深,会有更大的系统内存开销。因此在开发过程中,要尽可能减少布局嵌套,使布局更加扁平化。布局扁平化:选用正确的布局组件,不但可以去除中间嵌套的组件层级,减少组件数量,使代码更易于维护,也避免系统绘制更多的布局组件,达到优化性能、减少内存占用的目的。

尽量减少嵌套层级,嵌套层级越深,系统的内存开销越大。因此在开发过程中,要尽可能减少布局嵌套,使布局更加扁平化。例如Relativecontainer可以根据锚点来进行低嵌套层级复杂布局,而List和Grid等高级组件不但能使布局扁平化,而且支持懒加载等提升性能的方法,是更加推荐的布局方式。

组件复用

推荐场景:

  • 滑动场景下对同一类自定义组件的实例进行频繁的创建与销毁(配合LazyForeEach);
  • 反复切换条件渲染的控制分支,且控制分支中的组件子树结构相同;

列表中的组件复用机制如下:

  • 标记为@Reusable的组件从组件树上被移除时,组件和其对应的JsView对象都会被放入复用缓存中。
  • 当列表滑动新的Listltem将要被显示,List组件树上需要新建节点时,将会从复用缓存中查找可复用的组件节点,
  • 找到可复用节点并对其进行更新后添加到组件树中。从而节省了组件节点和JsView对象的创建时间。
@Reusable // 标记组件复用
@Component
struct Goodltems {
@State img: Resource = $r("app.media.photo61")
@State webimg?: string
@State hei:number=0
@State introduce: string="
@State price: string="
@State numb: string="
aboutToReuse(params){
this.webimg= params.webimg
this.img= params.img
this.hei= params.hei
this.introduce = params.introduce
this.price = params.price
this.numb=params.numb
build(){
}

精确控制状态变量的关联组件数

不推荐使用更新单个状态变量的形式自行控制多个组件更新时机(命令式);
推荐使用状态变量和组件一一绑定的方式,以数据的变更驱动组件的刷新(声明式)

 // 推荐
@Entry
@Campcnent
strutt CompA{
@State realStafe1:Array<number> =[4,1,3,2];
@StaterealState2: Color =Color.Yellow
build(){
 Column({space: 20}){
   ForEach(this.realState1,item => {
     Text(`${item}`)
 })
 Text("add item");
 .onClick(()=>{
   //改变realstate1触发UI视图更新
   this.realState1.push(this.realStatel[this.realState1.length - 1] + 1);
 })
 Text("chg color")
 .onClick(() =>{
   //改变realState2触发U视图更新
   this.realState2 =this.realState2 == Color.Yellow ? Color.Red : Color.Yellow;
  })
}
.backgroundColor(this.realState2)
.width(200).height(500)
 // 不推荐
@Entry
@Companent
struct CompA {
  DState needsupdate:boalean = true;
  realstate1: Array<nunber>=l4,1,3,21;/未使用状态变量装饰器
  realState2: Color= Color.Yellow
  updateUl(param any): any
  const triggerAGet = this.needsUpdate;
  return param
}
build(){
  Column({space: 20}){
  ForEach(this.updateUl(this.realState1), item => {Text(`${item}')
})
  Text("add item")
    .onClick(() => {
      //改变realstate1不会触发UI视图更新
      this.realStatel.push(this.realState1[this.realState1.length - 1] + 1);
      //触发UI视图更新
      this.needsUpdate =lthis.needsUpdate
  Text("chg color")
    .onClick(()=>{
    //改变realstate2 不会触发UI视图更新
    this.realState2 =this,realState2= Color.Yellow ? Color.Red : Color.Yellow
    //触发UI视图更新
    this.needsUpdate = lthis.needsUpdate
  })
  .backgroundColor(this.updateUllthis.realState2))width(200).height(500)

合理控制状态更新范围,避免关联刷新较大范围或者渲染较慢的组件

elass Info {
  name: striag =";
  userDefine: striag=":
  size:numbel =0;
  image:Ressurce | undefined = undefined
}
@Entcy
@Component
struct Index {
  @State userinfo: Info = new Info();

上面代码,如何info类里name变量,对系统来说相当于info这个状态变量更新,而info所关联的所有组件都会刷新。

在对象上谨慎使用状态变量关联

理解@Prop和@ObjectLink的区别:@Prop是深拷贝,@ObjectLink是浅拷贝。所以在@Prop和@ObjectLink使用效果相同的场景下,优先使用@0bjectlink的方式减少系统内存开销。

父组件传给子组件状态变量。@Prop是复制数据。@ObjectLink是做引用的拷贝,父子用同一个引用指针。

使用性能调优工具

  • ArkUI lnspector:用于检查和调试应用程序页面布局的情况
  • Launch Insight:录制和还原从启动应用,到显示首帧道程中的CPU、内存等资源使用情况,用于分析启动耗时长的问题。
  • Frame Insight:录制卡顿过程中的关键数据,标注出应用侧、RenderService (绘制侧)卡顿帧,用于分析应用卡顿、丢帧的问题高的热点代码段,用于分析卡顿、CPU 占用高、运行速度慢等问题。
  • Time Insight:通过周期性采集调用栈,识别 CPU 耗日
  • Allocation Insight:录制和分析内存分配记录,用于析内存峰值高,内存泄漏,内存不足导致应用被强杀等问题。象的分布,通过快照方式对比 ArkTs 对象分布区别,用于分析内存泄漏问题
  • Snapshot Insight:录制和分析应用程序中 ArkTS 对CPU 核频率、Trace 等数据,可用于分析卡顿、运行速度慢、应用无响应等问
  • CPU Insight:录制 CPU 调度事件、线程运行状态题。
  • Smart Perf:开源性能调优平台,支持对CPU调度、顷点、进程线程时间片、堆内存、帧率等数据进行采集和展示,展示方式为泳道图