鸿蒙 动态下载增强功能:产品特性按需分发
随着HarmonyOS应用的持续发展,应用的功能越来越丰富。但实际上,80%的用户使用时长都集中在20%的特性上,其余功能可能只面向部分用户。
为了避免用户首次下载应用耗时过长及过多占用用户空间,应用市场服务提供了按需分发的能力。
一、什么是按需分发?
一个应用程序被打包成多个安装包,安装包包含了所有的应用程序代码和静态资源。用户从应用市场下载的应用只包含基本功能的安装包,当用户需要使用增强功能时,相应安装包将会从服务器下载到设备上。
| 优势 | 说明 |
|---|---|
| 减少首次下载耗时 | 只下载基础包 |
| 节省用户空间 | 按需下载,不预先安装 |
| 提升用户体验 | 只下载用到的功能 |
二、限制
| 限制项 | 说明 |
|---|---|
| 应用上架 | 应用需要上架应用市场 |
| 设备支持 | Phone、Tablet、PC/2in1、TV(19+) |
| 模拟器支持 | ARM版本、X86版本模拟器支持接入调试 |
三、核心接口
| 接口 | 描述 |
|---|---|
getInstalledModule(moduleName) | 查询模块安装信息 |
createModuleInstallRequest(context) | 创建按需加载请求对象 |
addModule(moduleName) | 添加要按需加载的模块名 |
fetchModules(moduleInstallRequest) | 按需加载请求接口,异步返回结果 |
cancelTask(taskId) | 取消下载任务 |
showCellularDataConfirmation(context, taskId) | 流量提醒弹窗接口 |
on('moduleInstallStatus', callback, timeout) | 监听当前应用下载任务的进度 |
off('moduleInstallStatus', callback) | 取消监听 |
四、开发步骤
4.1 获取模块安装信息
import { moduleInstallManager } from '@kit.AppGalleryKit'; // 查询指定模块的安装信息 const moduleName: string = 'AModule'; const moduleInfo: moduleInstallManager.InstalledModule = moduleInstallManager.getInstalledModule(moduleName);4.2 创建按需加载的请求实例
import { moduleInstallManager } from '@kit.AppGalleryKit'; import type { common } from '@kit.AbilityKit'; // 获取上下文(只支持UIAbilityContext和ExtensionContext) const context: common.UIAbilityContext | common.ExtensionContext = this.getUIContext().getHostContext() as common.UIAbilityContext; // 创建请求对象 const myModuleInstallProvider = new moduleInstallManager.ModuleInstallProvider(); const myModuleInstallRequest = myModuleInstallProvider.createModuleInstallRequest(context);4.3 请求按需加载模块
// 添加要加载的模块名 const moduleNameA: string = 'AModule'; const moduleNameB: string = 'BModule'; const aResult = myModuleInstallRequest.addModule(moduleNameA); const bResult = myModuleInstallRequest.addModule(moduleNameB); // 发起按需加载请求 try { moduleInstallManager.fetchModules(myModuleInstallRequest) .then(() => { console.info('Succeeded in fetching Modules data.'); }) } catch (error) { console.error(`fetching Modules error: ${error.code}, ${error.message}`); }4.4 使用动态模块
步骤1:配置动态模块(AModulelib)
在动态模块的module.json5中设置deliveryWithInstall为false,标识该模块不会在应用安装时一起下载。
{ "module": { "name": "AModulelib", "deliveryWithInstall": false } }步骤2:定义动态模块功能
Calc.ets:
export function add(a: number, b: number) { return a + b; }DateComponent.ets:
@Component struct DateComponent { build() { Column() { Text('我是AModulelib中的组件') .margin(10); } .width(300) .backgroundColor(Color.Yellow); } } @Builder export function showDateComponent() { DateComponent() }Index.ets(导出):
export { add } from './src/main/ets/utils/Calc'; export { showDateComponent } from './src/main/ets/components/DateComponent';步骤3:配置动态依赖(entry模块)
在entry的oh-package.json5中添加:
{ "dynamicDependencies": { "AModulelib": "file:../AModulelib" } }步骤4:检查并加载动态模块
import { moduleInstallManager } from '@kit.AppGalleryKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { BusinessError, Callback } from '@kit.BasicServicesKit'; import { common } from '@kit.AbilityKit'; @Entry @Component struct Index { @BuilderParam AModulelibComponent: Function; @State countTotal: number = 0; @State isShow: boolean = false; build() { Column() { Button('调用增量模块中的add功能:3+6') .onClick(() => { this.initAModulelib(() => { import('AModulelib').then((ns: ESObject) => { this.countTotal = ns.add(3, 6); }).catch((error: BusinessError) => { hilog.error(0, 'TAG', `add error: ${error.code}, ${error.message}`); }); }) }); Text('计算结果:' + this.countTotal).margin(10); Button('调用增量模块中的showDateComponent功能') .onClick(() => { this.initAModulelib(() => { import('AModulelib').then((ns: ESObject) => { this.AModulelibComponent = ns.showDateComponent; this.isShow = true; }).catch((error: BusinessError) => { hilog.error(0, 'TAG', `showDateComponent error: ${error.code}, ${error.message}`); }); }) }); if (this.isShow) { this.AModulelibComponent() } } .width('100%') .height('100%') } // 检查是否已加载AModulelib包 private initAModulelib(successCallBack: Callback<void>): void { try { const result = moduleInstallManager.getInstalledModule('AModulelib'); if (result?.installStatus === moduleInstallManager.InstallStatus.INSTALLED) { hilog.info(0, 'TAG', 'AModulelib installed'); successCallBack(); } else { hilog.info(0, 'TAG', 'AModulelib not installed'); this.fetchModule('AModulelib', successCallBack); } } catch (error) { hilog.error(0, 'TAG', `getInstalledModule error: ${error.code}, ${error.message}`); } } // 监听下载进度 private onListenEvents(successCallBack: Callback<void>): void { const timeout = 3 * 60; // 3分钟,最大30分钟 moduleInstallManager.on('moduleInstallStatus', (data) => { if (data.taskStatus === moduleInstallManager.TaskStatus.INSTALL_SUCCESSFUL) { successCallBack(); this.showToastInfo('install success'); } }, timeout); } // 加载指定包 private fetchModule(moduleName: string, successCallBack: Callback<void>): void { try { const context = this.getUIContext().getHostContext() as common.UIAbilityContext; const moduleInstallProvider = new moduleInstallManager.ModuleInstallProvider(); const moduleInstallRequest = moduleInstallProvider.createModuleInstallRequest(context); moduleInstallRequest.addModule(moduleName); moduleInstallManager.fetchModules(moduleInstallRequest) .then((data) => { if (data.code === moduleInstallManager.RequestErrorCode.SUCCESS) { this.onListenEvents(successCallBack); } else { hilog.info(0, 'TAG', 'fetchModules failure'); } }) .catch((error: BusinessError) => { hilog.error(0, 'TAG', `fetchModules error: ${error.code}, ${error.message}`); }); } catch (error) { hilog.error(0, 'TAG', `handleFetchModules error: ${error.code}, ${error.message}`); } } private showToastInfo(msg: string) { this.getUIContext().getPromptAction().showToast({ message: msg, duration: 2000 }); } }五、接入调试功能
5.1 调试前提
产品特性按需分发提供了接入调试功能,支持开发者在接入过程中进行调试,应用无需上架应用市场。
5.2 调试步骤
| 步骤 | 操作 |
|---|---|
| 1 | 使用调试证书签名应用,本地编译构建entry.hap和AModulelib.hsp |
| 2 | 通过HDC命令或DevEco Studio安装基础包:hdc install entry.hap |
| 3 | 打开开发者调试模式:设置 → 机型 → 关于手机 → 连续点击软件版本7次 |
| 4 | 访问设备沙箱路径,创建cache/moduleinstall/AModulelib目录 |
| 5 | 将AModulelib.hsp上传至对应模块目录下(确保有读写权限) |
5.3 工作原理
安装包上传后,按照正常业务流程调用API,无需改动参数即可安装好模块调试包。监听到安装成功后,对应模块目录下的文件会被自动删除。
六、业务流程
用户下载基础包(entry.hap) ↓ 用户使用增强功能 ↓ 应用检查模块是否已安装 ↓ 未安装 → 调用fetchModules下载动态安装包 ↓ 动态安装包下载完成 ↓ 通过on接口告知用户下载结果 ↓ 使用动态import调用模块功能| 要点 | 说明 | |
|---|---|---|
| 配置关键 | deliveryWithInstall: false | 模块不随应用安装 |
| 依赖配置 | dynamicDependencies | entry模块动态依赖 |
| 检查安装 | getInstalledModule | 判断模块是否已安装 |
| 下载模块 | fetchModules | 按需下载 |
| 使用方式 | 动态import() | 加载模块功能 |
| 监听进度 | on('moduleInstallStatus') | 监听下载结果 |
开发流程
配置动态模块(deliveryWithInstall: false) ↓ 配置动态依赖(oh-package.json5) ↓ 检查模块是否已安装(getInstalledModule) ↓ 未安装则下载(fetchModules + on监听) ↓ 安装成功后动态import使用模块功能一句话
鸿蒙中产品特性按需分发通过将增强功能配置为deliveryWithInstall: false,在entry模块中配置dynamicDependencies,运行时通过getInstalledModule检查、fetchModules下载、动态import()使用,实现用户按需下载所需功能模块,减少首次下载时间和存储空间占用。
