#我的鸿蒙开发手记#HarmonyOS应用开发性能优化(篇三) 原创

FelixLu
发布于 2025-5-15 19:24
浏览
0收藏

1. 使用性能调优工具

  • ArkUIInspector:用于检查和调试应用程序页面布局的情况
  • Launch Insight: 录制和还原从启动应用,到显示首帧过程中的CPU、内存等资源使用情况,用于分析启动耗时长的问题。
  • Frame Insight:录制卡顿过程中的关键数据,标注出应用侧、RenderService侧卡顿帧, 用于分析应用卡顿、丢帧的问题。
  • Time Insight:通过周期性采集调用栈,识别CPU 耗时高的热点代码段,用于分析卡顿、CPU 占用高、运行速度慢等问题。
  • Allocation Insight:录制和分析内存分配记录,用于分析内存峰值高,内存泄漏,内存不足导致应用被强杀等问题。
  • Snapshot Insight:录制和分析应用程序中ArkTS对象的分布,通过快照方式对比ArkTS对象分布区别,用于分析内存泄漏问题。
  • CPU Insight:录制CPU 调度事件、线程运行状态、CPU 核频率、Trace 等数据,可用于分析卡顿、运行速度慢、应用无响应等问题。
  • Smart Perf_Host:开源性能调优平台,支持对CPU调度、频点、进程线程时间片、堆内存、帧率等数据进行采集和展示,展示方式为泳道图
  • HiDumper命令行:HiDumper是系统为开发、测试人员、IDE工具提供的系统信息获取工具,帮助开发者分析、定位问题。
  • 状态变量组件定位工具:开发者可以使用状态变量组件定位工具获取状态管理相关信息,例如自定义组件拥有的状态变量、状态变量的同步对象和关联组件等,了解状态变量影响UI的范围,写出高性能应用代码。

2. 其他性能调优方法

2.1. 使用Web的同层渲染提升性能

在使用Web组件加载H5页面时,经常会有长列表、视频等场景。由于Web目前最高只有60帧,想要更加流畅的体验,必须要将原生组件放到Web上。

同层渲染和非同层渲染显示Web页面的方式如下:

  1. Web页面开发者在页面中用Embed标签来表示使用原生系统组件。
  2. Embed标签创建独立Surface绘制环境,将此Surface返回给应用侧。
  3. 开发者收到Surface后,将Surface关联BuilderNode节点(原生button,原生视频等),BuilderNode子树渲染结果将绘制到Embed标签上。

2.2. 使用动态加载延后模块加载,提升启动速度

在有些主页场景,会跳转到各个复杂模块中去,有些模块并不是常用模块,我们可以使用动态延迟加载的方式,减少无用模块的加载。页面在引入依赖的加载模块时,如果不是首帧必须显示的内容模块,可以使用动态加载的方式(await import),延迟模块的加载时间,加快首帧显示的加载速度。

  • 优化前

import { pageOne, pagesOneData } from './pageOne'
import { pageTwo, pagesTwoData } from './pageTwo'
import { pageThree, pagesThreeData } from './pageThree'
import { pageFour, pagesFourData } from './pageFour'
// ...
import router from '@ohos.router'
  • 优化后:

async loadPageOne(key: string) {
  if (key == "pageOne") {
    let { PageOneLoader } = await import("../pages/PageLoader")
    this.PageOneLoader = PageOneLoader
  }
}

2.3. 合理使用RenderGroup

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

build() {
  Column() {
    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)
  .renderGroup(true)
}

2.4. 使用显隐控制进行页面缓存

控制元素显示与隐藏是一种常见的场景,使用Visibility.None、if条件判断等都能够实现该效果。其中if条件判断控制的是组件的创建、布局阶段,visibility属性控制的是元素在布局阶段是否参与布局渲染。使用时如果使用的方式不当,将引起性能上的问题。如果会频繁响应显示与隐藏的交互效果,建议使用切换Visibility.None/Visibility.Hidden和Visibility.Visible来控制元素显示与隐藏,在组件无需展示的时候进行缓存,提高性能。

build() {
  Column() {
    Button("Switch visible and hidden").onClick(() => {
      this.isVisible =!(this.isVisible)
    }).width('100%')
    Stack() {
      Scroll() {
        Column() {
          ForEach(this.data, (item: number) => {
            Image($r('app.media.icon')).width('25%').height('12.5%')
          }, (item: number) => item.toString())
        // 使用显隐控制切换,不会频繁创建与销毁组件
        }.visibility(this.isVisible? Visibility.Visible : Visibility.None)
      }
    }
  }
}

2.5. 减少渲染进程的冗余开销

下面通过一个动画场景的示例,演示如何有效减少渲染进程中的冗余开销,从而提升系统性能与界面流畅度。

  1. 组件转场动画推荐使用transition

组件转场动画指在组件出现和消失时做动画,有两种实现方式:

  • 属性动画+动画结束回调逻辑处理,即animateTo (不推荐)。

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
      }
    }}, () => {
      this.mOpacity = this.mOpacity === 1? 0 : 1
    })
  })
  • 组件转场动画transition (推荐) 。

build() {
  Column() {
    Row() {
      if (this.show) {
        Text('value')
          // 设置id,使转场可打断
          .id("myText")
          .transition(TransitionEffect.OPACITY.animation({duration: 1000}))
      }
    }
    .width("100%")
    .height(100)
    .justifyContent(FlexAlign.Center)
    Text("toggle state")
     .onClick(() => {
        // 通过transition,做透明度的出现或消失动画
        this.show =!this.show
      })
  }
}
  1. 合理使用动效—动画参数相同时使用同一个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 === Color.Yellow? Color.Red : Color.Yellow)
  })
}

func() {
  animateTo({curve: Curve.Sharp, duration: 1000}, () => {
    // 推荐
    this.w = (this.w === 100? 200 : 100)
    this.color = (this.color === Color.Yellow? Color.Red : Color.Yellow)
  })
}
  1. 合理使用动效—多次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.h = 100
          animateTo({curve: Curve.Sharp, duration: 1000}, () => {
            this.w = 200
          })
          this.color = Color.Yellow
          animateTo({curve: Curve.Linear, duration: 2000}, () => {
            this.color = Color.Red
          })
        })
    }
   .width("100%")
   .height("100%")
  }
}
  • 推荐

struct MyComponent {
  @State w: number = 100
  @State h: number = 50
  @State color: Color = Color.Yellow
  build() {
    Column() {
      Text("click")
       .height(this.h)
       .onClick(() => {
          this.w = 100
          animateTo({curve: Curve.Sharp, duration: 1000}, () => {
            this.w = (this.w === 100? 200 : 100)
          })
          animateTo({curve: Curve.Linear, duration: 2000}, () => {
            this.color = (this.color === Color.Yellow? Color.Red : Color.Yellow)
          })
        })
    }
   .width("100%")
   .height("100%")
  }
}


--THE END--


学习更多鸿蒙应用开发技能,请观看视频教程:

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2025-5-16 20:24:29修改
收藏
回复
举报
回复
    相关推荐