鸿蒙(HarmonyOS)性能优化实战-Swiper高性能开发

2024-04-23 23:04

本文主要是介绍鸿蒙(HarmonyOS)性能优化实战-Swiper高性能开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景

在应用开发中,Swiper 组件常用于翻页场景,比如:桌面、图库等应用。Swiper 组件滑动切换页面时,基于按需加载原则通常会在下一个页面将要显示时才对该页面进行加载和布局绘制,这个过程包括:

  • 如果该页面使用了@Component 装饰的自定义组件,那么自定义组件的 build 函数会被执行并创建内部的 UI 组件;

  • 如果使用了LazyForEach,会执行 LazyForEach 的 UI 生成函数生成 UI 组件;

  • 在 UI 组件构建完成后,会对 UI 组件进行布局测算和绘制。

针对复杂页面场景,该过程可能会持续较长时间,导致滑动过程中出现卡顿,对滑动体验造成负面影响,甚至成为整个应用的性能瓶颈。如在图库大图浏览场景中,若不使用预加载机制,每次都将在滑动开始的首帧去加载下一张图片,会导致首帧耗时过长甚至掉帧,拖慢应用性能。

为了解决上述问题,可以使用 Swiper 组件的预加载机制,利用主线程的空闲时间来提前构建和布局绘制组件,优化滑动体验。

使用场景

如果开发者的应用场景属于加载较为耗时的场景时,尤其是下列场景,推荐使用 Swiper 预加载功能。

  • Swiper 的子组件大于等于五个;

  • Swiper 的子组件具有复杂的动画;

  • Swiper 的子组件加载时需要执行网络请求等耗时操作;

  • Swiper 的子组件包含大量需要渲染的图像或资源。

Swiper 预加载机制说明

预加载机制是 Swiper 组件中一个重要的特性,允许 Swiper 滑动到下一个子组件之前提前加载后续页面的内容,其主要目的是提高应用滑动时的流畅性和响应速度。当用户尝试滑动到下一个子组件时,如果下一个子组件的内容已经提前加载完毕,那么滑动就会立即发生,否则 Swiper 组件需要在加载下一个子组件的同时处理滑动事件,对滑动体验造成负面影响。当前 Swiper 组件的预加载在用户滑动离手动效开始时触发,离手动效的计算在渲染线程中进行,因此主线程有空闲的时间可以进行预加载的操作。配合 LazyForEach 的按需加载和销毁能力,可以在优化滑动体验基础上节省内存占用。

使用指导

  • 预加载子组件的个数在cachedCount属性中配置。

Swiper 共 5 页,当开发者设置了 cacheCount 属性为 1 且 loop 属性为 false 时,预加载的结果如下:\


 Swiper 共 5 页,当开发者设置了 cacheCount 属性为 1 且 loop 属性为 true 时,预加载的结果如下:\

  • Swiper 组件的子组件使用LazyForEach动态加载和销毁组件。

示例

class MyDataSource implements IDataSource { // LazyForEach的数据源private list: number[] = [];constructor(list: number[]) {this.list = list;}totalCount(): number {return this.list.length;}getData(index: number): number {return this.list[index];}registerDataChangeListener(_: DataChangeListener): void {}unregisterDataChangeListener(): void {}
}@Component
struct SwiperChildPage { // Swiper的子组件@State arr: number[] = [];aboutToAppear(): void {for (let i = 1; i <= 100; i++) {this.arr.push(i);}}build() {Column() {List({ space: 20 }) {ForEach(this.arr, (index: number) => {ListItem() {Text(index.toString()).height('4.5%').fontSize(16).textAlign(TextAlign.Center).backgroundColor(0xFFFFFF)}.border({ width: 2, color: Color.Green })}, (index: number) => index.toString());}.height("95%").width("95%").border({ width: 3, color: Color.Red }).lanes({ minLength: 40, maxLength: 40 }).alignListItem(ListItemAlign.Start).scrollBar(BarState.Off)}.width('100%').height('100%').padding({ top: 5 });}
}@Entry
@Preview
@Component
struct SwiperExample {private dataSrc: MyDataSource = new MyDataSource([]);aboutToAppear(): void {let list: Array<number> = []for (let i = 1; i <= 10; i++) {list.push(i);}this.dataSrc = new MyDataSource(list);}build() {Column({ space: 5 }) {Swiper() {LazyForEach(this.dataSrc, (_: number) => {SwiperChildPage();}, (item: number) => item.toString());}.loop(false).cachedCount(1) // 提前加载后一项的内容.indicator(true).duration(100).displayArrow({showBackground: true,isSidebarMiddle: true,backgroundSize: 40,backgroundColor: Color.Orange,arrowSize: 25,arrowColor: Color.Black}, false).curve(Curve.Linear)}.width('100%').margin({ top: 5 })}
}

验证效果

为了更好地体现 Swiper 预加载机制带来的性能优化效果,用例采用下列前置条件:

  • Swiper 的子组件为带有 100 个 ListItem 的 List 组件;

  • Swiper 组件共有 10 个 List 子组件。

在该场景下,使用 Swiper 预加载机制可以为每个翻页动作节省约40%的时间,同时保证翻页时不丢帧,保证翻页的流畅度。

优化建议

由于组件构建和布局计算需要一定时间,cachedCount 的数量也不是设置得越大越好,过大的 cachedCount 可能会导致应用性能降低。当前 Swiper 组件滑动离手后的动效时间大约是 400ms,如果应用加载一个子组件的时间在 100ms~200ms 之间,为了在离手动效时间内完成组件的预加载,cachedCount 属性建议设置为 1 或 2,设置过大会导致主线程阻塞而产生卡顿。

那么方案可以继续优化,Swiper 组件有一个OnAnimationStart回调接口,切换动画开始时触发该回调。此时,主线程空闲,应用可以充分利用这段时间进行图片等资源的预加载,减少后续 cachedCount 范围内的节点预加载耗时。

示例

Swiper 子组件页面代码如下:

在子组件首次构建(生命周期执行到aboutToAppear)时,先判断 dataSource 中该 index 的数据是否有数据,若无数据则先进行资源加载,再构建节点。若有数据,则直接构建节点即可。

import image from '@ohos.multimedia.image';
import { MyDataSource } from './Index'@Component
export struct PhotoItem { //Swiper的子组件myIndex: number = 0;private dataSource: MyDataSource = new MyDataSource([]);context = getContext(this);@State imageContent: image.PixelMap | undefined = undefined;aboutToAppear(): void {console.info(`aboutToAppear` + this.myIndex);this.imageContent = this.dataSource.getData(this.myIndex)?.image;if (!this.imageContent) { // 先判断dataSource中该index的数据是否有数据,若无数据则先进行资源加载try {// 获取resourceManager资源管理器const resourceMgr = this.context.resourceManager;// 获取rawfile文件夹下item.jpg的ArrayBufferlet str = "item" + (this.myIndex + 1) + ".jpg";resourceMgr.getRawFileContent(str).then((value) => {// 创建imageSourceconst imageSource = image.createImageSource(value.buffer);imageSource.createPixelMap().then((value) => {console.log("aboutToAppear push" + this.myIndex)this.dataSource.addData(this.myIndex, { description: "" + this.myIndex, image: value })this.imageContent = value;})})} catch (err) {console.log("error code" + err);}}}build() {Column() {Image(this.imageContent).width("100%").height("100%")}}
}

Swiper 主页面的代码如下:

import Curves from '@ohos.curves';
import { PhotoItem } from './PhotoItem'
import image from '@ohos.multimedia.image';interface MyObject {description: string,image: image.PixelMap,
};export class MyDataSource implements IDataSource {private list: MyObject[] = []constructor(list: MyObject[]) {this.list = list}totalCount(): number {return this.list.length}getData(index: number): MyObject {return this.list[index]}registerDataChangeListener(listener: DataChangeListener): void {}unregisterDataChangeListener(listener: DataChangeListener): void {}addData(index: number, data: MyObject) {this.list[index] = data;}
}@Entry
@Component
struct Index {@State currentIndex: number = 0;cacheCount: number = 1swiperController: SwiperController = new SwiperController();private data: MyDataSource = new MyDataSource([]);context = getContext(this);aboutToAppear() {let list: MyObject[] = []for (let i = 0; i < 6; i++) {list.push({ description: "", image: this.data.getData(this.currentIndex)?.image })}this.data = new MyDataSource(list)}build() {Swiper(this.swiperController) {LazyForEach(this.data, (item: MyObject, index?: number) => {PhotoItem({myIndex: index,dataSource: this.data})})}.cachedCount(this.cacheCount).curve(Curves.interpolatingSpring(0, 1, 228, 30)).index(this.currentIndex).indicator(true).loop(false)// 在OnAnimationStart接口回调中进行预加载资源的操作.onAnimationStart((index: number, targetIndex: number) => {console.info("onAnimationStart " + index + " " + targetIndex);if (targetIndex !== index) {try {// 获取resourceManager资源管理器const resourceMgr = this.context.resourceManager;// 获取rawfile文件夹下item.jpg的ArrayBufferlet str = "item" + (targetIndex + this.cacheCount + 2) + ".jpg";resourceMgr.getRawFileContent(str).then((value) => {// 创建imageSourceconst imageSource = image.createImageSource(value.buffer);imageSource.createPixelMap().then((value) => {this.data.addData(targetIndex + this.cacheCount + 1, {description: "" + (targetIndex + this.cacheCount + 1),image: value})})})} catch (err) {console.log("error code" + err);}}}).width('100%').height('100%')}
}

总结

  • Swiper 组件的预加载机制与 LazyForEach 结合使用,能够达到最佳优化效果。

  • 预加载的 cachedCount 并非越大越好,需要结合单个子组件加载耗时来设置。假设一个子组件的加载耗时为 Nms,那么 cachedCount 推荐设置为小于 400/N。

  • 如果应用有非常高的性能优化需求,Swiper 预加载机制可搭配 OnAnimationStart 接口回调使用,进一步提升预加载的效率。

码牛课堂也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线。大家可以进行参考学习:https://qr21.cn/FV7h05

①全方位,更合理的学习路径
路线图包括ArkTS基础语法、鸿蒙应用APP开发、鸿蒙能力集APP开发、次开发多端部署开发、物联网开发等九大模块,六大实战项目贯穿始终,由浅入深,层层递进,深入理解鸿蒙开发原理!

②多层次,更多的鸿蒙原生应用
路线图将包含完全基于鸿蒙内核开发的应用,比如一次开发多端部署、自由流转、元服务、端云一体化等,多方位的学习内容让学生能够高效掌握鸿蒙开发,少走弯路,真正理解并应用鸿蒙的核心技术和理念。

③实战化,更贴合企业需求的技术点
学习路线图中的每一个技术点都能够紧贴企业需求,经过多次真实实践,每一个知识点、每一个项目,都是码牛课堂鸿蒙研发团队精心打磨和深度解析的成果,注重对学生的细致教学,每一步都确保学生能够真正理解和掌握。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:https://qr21.cn/FV7h05

如何快速入门:

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

开发基础知识:https://qr21.cn/FV7h05

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

基于ArkTS 开发:https://qr21.cn/FV7h05

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

鸿蒙开发面试真题(含参考答案):https://qr21.cn/FV7h05

大厂鸿蒙面试题::https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

这篇关于鸿蒙(HarmonyOS)性能优化实战-Swiper高性能开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

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

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

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设