【最新鸿蒙开发之性能优化——动态加载和延迟加载】

embedded/2024/11/18 16:18:55/

大家好,我是学徒小z,在经历了一段时间项目开发中,我也渐渐意识到了性能的重要性,今天就分享一篇优化应用运行性能的文章,话不多说,开干!

引言

延时触发操作与延迟加载的简介

  • 动态加载(动态import)是一种模块加载机制,允许应用程序在运行时按照实际需求去加载相关模块。在某些条件满足时(比如用户交互时,或分支切换时)再加载特定模块,可以减少初始化import的加载时间和资源消耗,这将有助于提高应用程序的内存性能和响应速度。
  • 以通过延迟加载 Lazy-Import的方法延缓对这些冗余文件的加载,使待加载文件在冷启动阶段不被加载,而在后续导出变量被真正使用时再同步加载执行文件,节省资源以提高应用冷启动性能。
  • Lazy-Import与动态加载都可以实现主动延后特定文件的执行时间,帮助设备均摊性能消耗,缓解特定时段性能压力的能力。

动态加载(Dynamic Import)

1. 动态加载适用场景

动态加载是指在代码运行时,根据需要按需导入模块的技术。它适用于以下场景:

  • 性能优化场景

    • 当模块明显降低代码加载速度

    • 当模块占用大量系统内存

    • 使用频率较低的功能模块

  • 按需加载场景

    • 模块在运行时才能确定是否需要

    • 模块需要异步获取

    • 模块路径需要动态构建

  • 条件执行场景

    • 模块中的代码只在特定条件下才需要执行
    • 模块的功能取决于用户的选择或系统状态

2. 动态加载的业务扩展

  • 条件延迟加载
    条件延迟加载允许根据特定条件决定是否加载模块:
// 根据用户权限加载不同功能
if (userHasAdminAccess) {const adminModule = await import('./admin/features');adminModule.initAdminFeatures();
}
  • 反射功能

反射(Reflection)是指程序在运行时能够访问、检测和修改它本身状态或行为的能力。

​ 在鸿蒙开发中,动态加载支持简单的反射功能:

// 动态调用类和方法
import('myModule').then((module) => {// 通过字符串动态访问类const className = 'UserManager';const ClassType = module[className];// 通过字符串动态访问方法const methodName = 'getUserInfo';const instance = new ClassType();instance[methodName]();
});

3. 动态加载的实现方案与关键点

  • 基本实现方式
    动态加载支持两种主要的实现方式:
  1. 常量表达式导入
// 直接使用字符串字面量
import('./modules/feature').then(module => {module.doSomething();
});
  1. 变量表达式导入
const modulePath = getModulePath(); // 动态确定模块路径
import(modulePath).then(module => {module.doSomething();
});

4. 配置要点

​ 当使用变量表达式进行动态导入时,需要在项目配置文件中添加特殊配置:

{"buildOption": {"arkOptions": {"runtimeOnly": {"packages": ["moduleA", "moduleB"],  // 需要动态导入的模块"sources": ["./src/feature.ets"]     // 需要动态导入的文件}}}
}

5. HAR模块间动态import依赖解耦

当应用包含多个HAR包,且HAR包之间依赖关系比较复杂。在IDE中配置依赖关系时,可能会形成循环依赖。这时,如果HAR之间的依赖关系中仅有变量动态import,可以将HAR包之间直接依赖关系转移到HAP/HSP中配置,HAR包之间无需配置依赖关系,从而达到HAR包间依赖解耦的目的。如下示意图:

image-20241117215421513

HAR之间依赖关系转移到HAP/HSP后

image-20241117215437871

  1. 仅适用于本地源码HAR包间的循环依赖。
  2. 被转移的HAR间只能通过变量动态import,不支持静态import或常量动态import。
  3. 转移依赖时,需要同时转移dependenciesruntimeOnly配置。
  4. HSP不支持转移依赖。
  5. 转移依赖的链路上只能有HAR,不能跨越HSP。

实例:

​ har1对har2的依赖dependencies和runtimeOnly配置转移到HAP中,har1不需要配置对har2的dependencies和runtimeOnly配置:

// HAP's oh-package.json5
"dependencies": {"har1": "file:../har1","har2": "file:../har2"
}// HAP's build-profile.json5
"buildOption": {"arkOptions": {"runtimeOnly": {"packages": ["har1","har2"]}}
}// HAP's src/main/ets/pages/Index.ets
let harName = 'har1';
import(harName).then((ns:ESObject) => {console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5));
});// har1's Index.ets
export { addHar1 } from './src/main/ets/utils/Calc'// har1's src/main/ets/utils/Calc.ets
export function addHar1(a:number, b:number):number {let c = a + b;console.info('DynamicImport I am har1, %d + %d = %d', a, b, c);let harName = 'har2';import(harName).then((ns:ESObject) => {console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5));});return c;
}// har2's Index.ets
export { addHar2 } from './src/main/ets/utils/Calc'// har2's src/main/ets/utils/Calc.ets
export function addHar2(a:number, b:number):number {let c = a + b;console.info('DynamicImport I am har2, %d + %d = %d', a, b, c);return c;
}

延迟加载(Lazy-Import)

1. 延迟加载的功能特性与使用方式

延迟加载特性可使待加载文件在冷启动阶段不被加载(但模块解析依然会在冷启动时触发),直至应用程序实际运行过程中需要用到这些组件时,才按需同步加载相关文件,从而缩短应用冷启动耗时

  • 需要注意的是,后续执行的加载是同步加载,有可能会阻塞任务执行(如点击任务,触发了延迟加载,那么运行时会去执行冷启动未加载的文件,从而增加耗时。
  • 使用lazy-import延迟加载
    // main.ets   import lazy { a } from "./mod1";    // "mod1" 未执行import { c } from "./mod2";         // "mod2" 执行// ...console.info("main executed");while (false) {let xx = a;}// mod1.etsexport let a = "mod1 executed"console.info(a);// mod2.etsexport let c = "mod2 executed"console.info(c);//执行结果
//   mod2 executed
//   main executed

2. 延迟加载的场景行为与语法规格

lazy-import支持如下指令实现:

语法ModuleRequestImportNameLocalNameAPI12是否支持lazy加载
import lazy { x } from “mod”;“mod”“x”“x”支持
import lazy { x as v } from “mod”;“mod”“x”“v”支持
  • 延迟加载的错误示例

        export lazy var v;                  // 编译器提示报错:应用编译报错export lazy default function f(){}; // 编译器提示报错:应用编译报错export lazy default function(){};   // 编译器提示报错:应用编译报错export lazy default 42;             // 编译器提示报错:应用编译报错export lazy { x };                    // 编译器提示报错:应用编译报错export lazy { x as v };               // 编译器提示报错:应用编译报错export lazy { x } from "mod";         // 编译器提示报错:应用编译报错export lazy { x as v } from "mod";    // 编译器提示报错:应用编译报错export lazy * from "mod";           // 编译器提示报错:应用编译报错import lazy v from "mod";           // 编译器提示报错:应用编译报错import lazy * as ns from "mod";     // 编译器提示报错:应用编译报错
    

3. 使用场景

下述例子中A文件被引用,在应用启动到点击按钮的这段时间里,A文件并没有被实际执行,在冷启动阶段加载A文件的行为属于冗余。

// A为任意可以被引入的ets文件
import { A } from "./A"@Entry
@Component
struct Index {build() {RelativeContainer() {Button('点击执行A').onClick(() => {console.log('执行A' + A)})}// ...}
}

通过抓取Trace图查看调用栈可发现,应用在冷启动时加载了A文件。

技术对比与应用选择:

  • Lazy-Import与Dynamic Import的区别
动态加载Lazy-Import
语法示例let A= await import(“./A”);import lazy { A } from “./A”
性能开销创建异步任务开销。执行到动态加载时,触发依赖模块的模块解析+源码执行。Lazy-Import的模块解析耗时在冷启动依旧会触发遍历。导入的变量A被使用到时,触发模块的源码执行。
使用位置代码块/运行逻辑中使用需要写在源码开头
是否可以运行时拼接待加载模块名
加载时序异步同步

http://www.ppmy.cn/embedded/138586.html

相关文章

SpringSecurity+jwt+captcha登录认证授权总结

SpringSecurityjwtcaptcha登录认证授权总结 版本信息: springboot 3.2.0、springSecurity 6.2.0、mybatis-plus 3.5.5 认证授权思路和流程: 未携带token,访问登录接口: 1、用户登录携带账号密码 2、请求到达自定义Filter&am…

vue学习第8章(vue的购物车案例)

🎉🎉🎉欢迎来到我的博客,我是一名自学了2年半前端的大一学生,熟悉的技术是JavaScript与Vue.目前正在往全栈方向前进, 如果我的博客给您带来了帮助欢迎您关注我,我将会持续不断的更新文章!!!🙏🙏🙏 文章目录…

[含文档+PPT+源码等]精品大数据项目-python基于Spark实现的新闻推荐系统的设计与实现

一、新闻行业的快速发展与数据量的激增 新闻发布的优化与需求增长: 随着媒体和网络技术的快速发展,新闻发布方式不断优化,新闻行业迎来了前所未有的发展机遇。新闻内容的生产和传播速度加快,用户获取新闻的途径也更加多样化。同时…

单片机中的BootLoader(重要的概念讲解)

文章目录 一、链接地址和执行地址1. 链接地址(Load Address)2. 执行地址(Execution Address)链接地址与执行地址的关系实际工作流程总结二、相对跳转和绝对跳转1. 相对跳转(Relative Jump)2. 绝对跳转(Absolute Jump)3. `BX` 和 `BL` 指令总结三、散列文件1. 散列文件的…

【java】链表:判断链表是否成环

问题: 分析: 这里我们还是定义快慢双指针 。 如果有环,快慢指针一定会相遇。 // 构建成环链表public void makeCircle(){Node node1new Node(1);Node node2new Node(2);Node node3new Node(5);Node node4new Node(6);Node node5new …

什么是HTTP,什么是HTTPS?HTTP和HTTPS都有哪些区别?

什么是 HTTP? HTTP(Hypertext Transfer Protocol,超文本传输协议)是一种应用层协议,用于在互联网上进行数据通信。它定义了客户端(通常是浏览器)和服务器之间的请求和响应格式。HTTP 是无状态的…

Java爬虫:获取商品历史价格信息 API 数据

穿越时空的购物侦探 引言 如果你以为Java只能用来制作乏味的桌面应用,那你一定没见识过它的另一面——一个能够穿越时空的购物侦探!今天,我们就来聊聊如何用Java编写一个爬虫,这个爬虫不仅能获取商品的当前价格,还能…

六、对象属性方法及增删改查、遍历对象和对象数组、Math.random()随机数、数据类型存储、变量声明

1. 对象_属性和方法 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </h…