鸿蒙 动态下载增强功能:产品特性按需分发
随着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.etsexport function add(a: number, b: number) { return a b; }DateComponent.etsComponent 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功能:36) .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: Callbackvoid): 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: Callbackvoid): 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: Callbackvoid): 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.hsp2通过HDC命令或DevEco Studio安装基础包hdc install entry.hap3打开开发者调试模式设置 → 机型 → 关于手机 → 连续点击软件版本7次4访问设备沙箱路径创建cache/moduleinstall/AModulelib目录5将AModulelib.hsp上传至对应模块目录下确保有读写权限5.3 工作原理安装包上传后按照正常业务流程调用API无需改动参数即可安装好模块调试包。监听到安装成功后对应模块目录下的文件会被自动删除。六、业务流程用户下载基础包entry.hap ↓ 用户使用增强功能 ↓ 应用检查模块是否已安装 ↓ 未安装 → 调用fetchModules下载动态安装包 ↓ 动态安装包下载完成 ↓ 通过on接口告知用户下载结果 ↓ 使用动态import调用模块功能要点说明配置关键deliveryWithInstall: false模块不随应用安装依赖配置dynamicDependenciesentry模块动态依赖检查安装getInstalledModule判断模块是否已安装下载模块fetchModules按需下载使用方式动态import()加载模块功能监听进度on(moduleInstallStatus)监听下载结果开发流程配置动态模块deliveryWithInstall: false ↓ 配置动态依赖oh-package.json5 ↓ 检查模块是否已安装getInstalledModule ↓ 未安装则下载fetchModules on监听 ↓ 安装成功后动态import使用模块功能一句话鸿蒙中产品特性按需分发通过将增强功能配置为deliveryWithInstall: false在entry模块中配置dynamicDependencies运行时通过getInstalledModule检查、fetchModules下载、动态import()使用实现用户按需下载所需功能模块减少首次下载时间和存储空间占用。