鸿蒙OpenHarmony【动态加载】 ArkTS运行
动态加载
该动态导入机制采用条件延迟加载策略,并结合部分代码反射能力。该机制能够集成HSP模块、HAR模块、OHPM包以及Native库等基础组件;同时,在变量范围内进行动态导入时仍可实现模块间的解耦。
技术适用场景介绍
在应用开发中存在一些特定场景下(或称情况下),如果开发者希望在应用开发中根据特定条件或按需加载相应的模块(或子模块),则可以通过动态加载机制实现(或选择)这种功能(或效果)。通常建议采用动态导入机制来替代静态导入方式(或策略)。以下是一些可能需要采用动态导入机制的场景:
- 当静态导入的模块显著提升了代码运行效率的同时其适用性较低或者当前无需立即调用。
- 静态导入机制通常会导致程序资源消耗增加但当这种资源占用较低时可能更适合特定场景。
- 在初始化阶段若目标模块尚未引入则需采用延迟加载策略。
- 只有当外部库文件已预先解析完成才允许对其依赖关系进行明确声明这体现了静态导入机制的特点。
- 某些外部库文件可能会引入非预期行为如潜在影响或异常处理逻辑这些特殊效果通常会在特定条件下才会生效。
业务扩展场景介绍
动态导入在业务中不仅能够实现条件延迟加载的功能,并且还支持部分反射功能。
具体来说,在HAP中使用动态导入功能时会调用静态成员函数staticAdd()和成员函数instanceAdd()等接口,并通过调用全局方法addHarlibrary()来完成相关操作。
// harlibrary's src/main/ets/utils/Calc.ets
export class Calc {
public static staticAdd(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am harlibrary in staticAdd, %d + %d = %d', a, b, c);
return c;
}
public instanceAdd(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am harlibrary in instanceAdd, %d + %d = %d', a, b, c);
return c;
}
}
export function addHarlibrary(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am harlibrary in addHarlibrary, %d + %d = %d', a, b, c);
return c;
}
AI生成项目

// harlibrary's Index.ets
export { Calc, addHarlibrary } from './src/main/ets/utils/Calc'
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"harlibrary": "file:../harlibrary"
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
import('harlibrary').then((ns:ESObject) => {
ns.Calc.staticAdd(8, 9); // 调用静态成员函数staticAdd()
let calc:ESObject = new ns.Calc(); // 实例化类Calc
calc.instanceAdd(10, 11); // 调用成员函数instanceAdd()
ns.addHarlibrary(6, 7); // 调用全局方法addHarlibrary()
// 使用类、成员函数和方法的字符串名字进行反射调用
let className = 'Calc';
let methodName = 'instanceAdd';
let staticMethod = 'staticAdd';
let functionName = 'addHarlibrary';
ns[className][staticMethod](12, 13); // 调用静态成员函数staticAdd()
let calc1:ESObject = new ns[className](); // 实例化类Calc
calc1[methodName](14, 15); // 调用成员函数instanceAdd()
ns[functionName](16, 17); // 调用全局方法addHarlibrary()
});
AI生成项目

动态import实现方案介绍
基于输入参数的类型划分,动态导入功能可划分为动态导入常量表达式与动态导入变量表达式两大特性规范。其中包含以下规范内容的列表。
| 动态import场景 | 动态import详细分类 | 说明 |
|---|---|---|
| 本地工程模块 | 动态import模块内文件路径 | 要求路径以./或…/开头 |
| 本地工程模块 | 动态import HSP模块名 | - |
| 本地工程模块 | 动态import HSP模块文件路径 | 暂仅支持动态import常量表达式,不支持动态import变量表达式 |
| 本地工程模块 | 动态import HAR模块名 | - |
| 本地工程模块 | 动态import HAR模块文件路径 | 暂仅支持动态import常量表达式,不支持动态import变量表达式 |
| 远程包 | 动态import远程HAR模块名 | - |
| 远程包 | 动态import ohpm包名 | - |
| API | 动态import @system.* | - |
| API | 动态import @ohos.* | - |
| API | 动态import @arkui-x.* | - |
| 模块Native库 | 动态import libNativeLibrary.so | - |
注:
- 当前所有import中使用的名称都是来自oh-package.json5包中的别名;
- 本地模块在oh-package.json5包中的 dependencies 中设置名称的一致性建议应满足以下条件:moduleName、packageName及被导入名称三者需保持一致。其中moduleName指被导入HSP/HAR文件中的module.json5所配置的名字;packageName指被导入HSP/HAR文件中的oh-package.json5所配置的名字。
- 导入一个名称实际上是导入该名称对应的入口文件(一般为index.ets.ts)。
动态import实现中的关键点
动态import常量表达式
动态导入常量表达式即指动态导入时入参为固定不变的情况。
具体而言,在使用HAP引用其他模块或API时,我们可以清晰地展示其典型应用方式。
说明:本文示例代码中的Index.ets等路径基于当前DevEco Studio的模块配置进行设置。如有后续变化,请优化相应位置及其它相关文件的相对路径。
HAP常量动态import HAR模块名
// HAR's Index.ets
export function add(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c);
return c;
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
import('myHar').then((ns:ESObject) => {
console.info(ns.add(3, 5));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"myHar": "file:../myHar"
}
AI生成项目
HAP常量动态import HAR模块文件路径
// HAR's Index.ets
export function add(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c);
return c;
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
import('myHar/Index').then((ns:ESObject) => {
console.info(ns.add(3, 5));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"myHar": "file:../myHar"
}
AI生成项目
HAP常量动态import HSP模块名
// HSP's Index.ets
export function add(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c);
return c;
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
import('myHsp').then((ns:ESObject) => {
console.info(ns.add(3, 5));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"myHsp": "file:../myHsp"
}
AI生成项目
HAP常量动态import HSP模块名文件路径
// HSP's Index.ets
export function add(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c);
return c;
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
import('myHsp/Index').then((ns:ESObject) => {
console.info(ns.add(3, 5));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"myHsp": "file:../myHsp"
}
AI生成项目
HAP常量动态import远程HAR模块名
// HAP's src/main/ets/pages/Index.ets
import('@ohos/crypto-js').then((ns:ESObject) => {
console.info('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"@ohos/crypto-js": "2.0.3-rc.0"
}
AI生成项目
HAP常量动态import ohpm包
// HAP's src/main/ets/pages/Index.ets
import('json5').then((ns:ESObject) => {
console.info('DynamicImport json5');
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"json5": "1.0.2"
}
AI生成项目
HAP常量动态import自己的单文件
// HAP's src/main/ets/Calc.ets
export function add(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am a HAP, %d + %d = %d', a, b, c);
return c;
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
import('../Calc').then((ns:ESObject) => {
console.info(ns.add(3, 5));
});
AI生成项目
HAP常量动态import自己的Native库
// libnativeapi.so's index.d.ts
export const add: (a:number, b:number) => number;
AI生成项目
// HAP's src/main/ets/pages/Index.ets
import('libnativeapi.so').then((ns:ESObject) => {
console.info('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"libnativeapi.so": "file:./src/main/cpp/types/libnativeapi"
}
AI生成项目
HAP常量动态import加载API
// HAP's src/main/ets/pages/Index.ets
import('@system.app').then((ns:ESObject) => { ns.default.terminate(); });
import('@system.router').then((ns:ESObject) => { ns.default.clear(); });
import('@ohos.curves').then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); });
import('@ohos.matrix4').then((ns:ESObject) => { ns.default.identity(); });
import('@ohos.hilog').then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); });
AI生成项目
动态import变量表达式
在DevEco Studio中,默认情况下各模块通过oh-package.json5文件中的dependencies键进行配置以实现相互依赖关系。该dependencies列表中的所有本地模块会自动完成安装操作而无需人工干预(远程模块则需手动下载),但默认情况下不会主动参与编译过程。当执行HAP/HSP编译操作时,默认会以入口文件(通常为Index.ets.ts)开始搜索相关依赖关系,在找到相应的模块或文件后才会被包含进编译流程中来生成方舟字节码。值得注意的是,在涉及变量动态导入的情况下,默认状态无法解析其具体内容因而无法加入编译步骤;为了实现此类情况下的功能扩展,则需要额外设置一个名为runtimeOnly的编译选项来指定动态导入变量的实际模块名称或文件路径
1. runtimeOnly字段schema配置格式
向HAP/HSP/HAR构建过程中位于build-profile.json5中的buildOption字段中新增runtimeOnly字段,并且该字段仅在变量动态导入时进行配置。需要注意的是:静态导入和常量动态导入无需设置相关参数;例如,在以下实例中详细说明了如何配置通过变量动态导入其他模块以及自定义单文件模块的实现方法。
// 变量动态import其他模块myHar
let harName = 'myHar';
import(harName).then(……);
// 变量动态import本模块自己的单文件src/main/ets/index.ets
let filePath = './Calc';
import(filePath).then(……);
AI生成项目
对应的runtimeOnly配置:
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [ "myHar" ] // 配置本模块变量动态import其他模块名,要求与dependencies中配置的名字一致。
"sources": [ "./src/main/ets/utils/Calc.ets" ] // 配置本模块变量动态import自己的文件路径,路径相对于当前build-profile.json5文件。
}
}
}
AI生成项目
"packages":用于配置本模块变量动态导入其他模块的名字。
"sources":用于配置本模块变量动态导入自己文件路径的位置信息。
2. 使用实例
HAP变量动态import HAR模块名
// HAR's Index.ets
export function add(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c);
return c;
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
let packageName = 'myHar';
import(packageName).then((ns:ESObject) => {
console.info(ns.add(3, 5));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"myHar": "file:../myHar"
}
AI生成项目
// HAP's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [
"myHar" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
]
}
}
}
AI生成项目

HAP变量动态import HSP模块名
// HSP's Index.ets
export function add(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c);
return c;
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
let packageName = 'myHsp';
import(packageName).then((ns:ESObject) => {
console.info(ns.add(3, 5));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"myHsp": "file:../myHsp"
}
AI生成项目
// HAP's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [
"myHsp" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
]
}
}
}
AI生成项目

HAP变量动态import远程HAR模块名
// HAP's src/main/ets/pages/Index.ets
let packageName = '@ohos/crypto-js';
import(packageName).then((ns:ESObject) => {
console.info('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"@ohos/crypto-js": "2.0.3-rc.0"
}
AI生成项目
// HAP's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [
"@ohos/crypto-js" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
]
}
}
}
AI生成项目

HAP变量动态import ohpm包
// HAP's src/main/ets/pages/Index.ets
let packageName = 'json5';
import(packageName).then((ns:ESObject) => {
console.info('DynamicImport json5');
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"json5": "1.0.2"
}
AI生成项目
// HAP's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [
"json5" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
]
}
}
}
AI生成项目

HAP变量动态import自己的单文件
// HAP's src/main/ets/Calc.ets
export function add(a:number, b:number):number {
let c = a + b;
console.info('DynamicImport I am a HAP, %d + %d = %d', a, b, c);
return c;
}
AI生成项目
// HAP's src/main/ets/pages/Index.ets
let filePath = '../Calc';
import(filePath).then((ns:ESObject) => {
console.info(ns.add(3, 5));
});
AI生成项目
// HAP's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"sources": [
"./src/main/ets/Calc.ets" // 仅用于使用变量动态import模块自己单文件场景,静态import或常量动态import无需配置。
]
}
}
}
AI生成项目

HAP变量动态import自己的Native库
// libnativeapi.so's index.d.ts
export const add: (a:number, b:number) => number;
AI生成项目
// HAP's src/main/ets/pages/Index.ets
let soName = 'libnativeapi.so';
import(soName).then((ns:ESObject) => {
console.info('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3));
});
AI生成项目
// HAP's oh-package.json5
"dependencies": {
"libnativeapi.so": "file:./src/main/cpp/types/libnativeapi"
}
AI生成项目
// HAP's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [
"libnativeapi.so" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
]
}
}
}
AI生成项目

HAP变量动态import加载API
// HAP's src/main/ets/pages/Index.ets
let packageName = '@system.app';
import(packageName).then((ns:ESObject) => { ns.default.terminate(); });
packageName = '@system.router';
import(packageName).then((ns:ESObject) => { ns.default.clear(); });
packageName = '@ohos.curves';
import(packageName).then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); });
packageName = '@ohos.matrix4';
import(packageName).then((ns:ESObject) => { ns.default.identity(); });
packageName = '@ohos.hilog';
import(packageName).then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); });
AI生成项目

变量动态import加载API时无需配置runtimeOnly。
HAR模块间动态import依赖解耦
当应用程序集成多个HarmonyArchive(HAR)包时,并且这些HarmonyArchive之间存在较为复杂的相互依赖关系。在开发环境中配置这些HarmonyArchive的相互依存性时,则有可能出现循环依存的情况。此时,在满足特定条件的前提下——即每个HarmonyArchive仅进行变量范围内的动态导入——可以将各HarmonyArchive之间的直接依存关系转移至Heap-Attached Pages(HAP)/Shared Pages(SHP)中进行管理处理。这样一来,在HarmonyArchive内部则无需再建立彼此之间的直接依存关系——因为它们已成功地被分配到独立的Heap-Attached Pages/Shared Pages空间中——从而达到了对各个HarmonyArchive间的相互依存性进行解耦的目的。

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

1. 使用限制
仅限本地源码HAR包之间形成循环依赖时可使用该规避方案。
发生依赖关系的HAR之间仅能以变量形式进行动态导入操作,并不支持静态导入以及常量形式的动态导入
转移依赖时,dependencies和runtimeOnly依赖配置要同时转移。
在这一流程中,HAP之后依次是 HSP1, HSP2, HSP3, 其中,HSP2 和 HSP3 无法直接回到 HAP。
依赖关系在整体链路上仅限于使用 HAR 而不能跳过 HSP 转移。
具体来说:
\text{HAP} \rightarrow \text{HAR1} \rightarrow \text{HAR2} \rightarrow \text{HSP} \rightarrow \text{HA R3} \rightarrow \text{HA R4}
可将 HA1 对 HA2 的依赖转移至 HA P 中(即 HAP 上),同样地, HA R3 与 HA R4 之间的依存关系也能转移到 H S P 中(即 HSP 上)。然而, HA R3 或 HA R4 不可转移至 HA P 中(即 HAP 上)。
2. 使用实例
以下是一个案例:HAP变量动态导入一个HAR文件har1;随后har1变量又动态导入了一个新增的HAR文件har2。
// HAP's oh-package.json5
"dependencies": {
"har1": "file:../har1"
}
AI生成项目
// HAP's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [
"har1" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
]
}
}
}
AI生成项目

// 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));
});
AI生成项目
// har1's oh-package.json5
"dependencies": {
"har2": "file:../har2"
}
AI生成项目
// har1's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [
"har2" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
]
}
}
}
AI生成项目

// har1's Index.ets
export { addHar1 } from './src/main/ets/utils/Calc'
AI生成项目
// 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;
}
AI生成项目

// har2's Index.ets
export { addHar2 } from './src/main/ets/utils/Calc'
AI生成项目
// 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;
}
AI生成项目
har1基于har2的依赖dependencies和runtimeOnly settings迁移到HAP中; har1无需为har2配置这些 dependencies 和 runtimeOnly settings:
// HAP's oh-package.json5
"dependencies": {
"har1": "file:../har1",
"har2": "file:../har2"
}
AI生成项目
// HAP's build-profile.json5
"buildOption": {
"arkOptions": {
"runtimeOnly": {
"packages": [
"har1",
"har2"
]
}
}
}
AI生成项目

// 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));
});
AI生成项目
// har1's Index.ets
export { addHar1 } from './src/main/ets/utils/Calc'
typescript
AI生成项目
// 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;
}
AI生成项目

// har2's Index.ets
export { addHar2 } from './src/main/ets/utils/Calc'
typescript
AI生成项目
// 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;
}
```**
AI生成项目
