文档管理中心

产品特性按需分发

场景介绍

随着HarmonyOS应用的持续发展,应用的功能将越来越丰富,实际上80%的用户使用时长都会集中在20%的特性上,其余的功能可能也仅仅是面向部分用户。为了避免用户首次下载应用耗时过长,及过多占用用户空间,应用市场服务提供按需分发的能力,支持用户按需动态下载自己所需的增强特性。

基本概念

按需分发:一个应用程序被打包成多个安装包,安装包包含了所有的应用程序代码和静态资源。用户从应用市场下载的应用只包含基本功能的安装包,当用户需要使用增强功能时,相应安装包将会从服务器下载到设备上(应用发布请参考发布HarmonyOS应用)。

业务流程

  1. 用户下载A应用的基础包。
  2. 用户使用增强功能。
  3. 应用通过API下载动态安装包。
  4. 下载完成。
  5. 告知用户下载结果。

接口说明

产品特性按需分发场景提供以下接口,具体API说明详见接口文档

展开

接口名

描述

getInstalledModule(moduleName: string): InstalledModule

查询模块安装信息接口。

createModuleInstallRequest(context: common.UIAbilityContext | common.ExtensionContext): ModuleInstallRequest

创建按需加载请求对象。

addModule((moduleName: string): ReturnCode

添加要按需加载的模块名。

fetchModules(moduleInstallRequest: ModuleInstallRequest): Promise<ModuleInstallSessionState>

按需加载请求接口,异步返回结果。

cancelTask(taskId: string): ReturnCode

取消下载任务接口。

showCellularDataConfirmation(context: common.UIAbilityContext | common.ExtensionContext, taskId: string): ReturnCode

流量提醒弹窗接口。

on(type: 'moduleInstallStatus', callback: Callback<ModuleInstallSessionState>, timeout: number): void

监听当前应用下载任务的进度。

off(type: 'moduleInstallStatus', callback?: Callback<ModuleInstallSessionState>): void

取消监听当前应用下载任务的进度。

开发步骤

获取模块安装信息

  1. 导入moduleInstallManager模块及相关公共模块。

    收起
    自动换行
    深色代码主题
    复制
    1. import { moduleInstallManager } from '@kit.StoreKit';

  2. 构造参数。

    入参为需要查询的模块名称。
    收起
    自动换行
    深色代码主题
    复制
    1. const moduleName: string = 'AModule';

  3. 调用getInstalledModule方法,将步骤2中构造的参数传入模块中的getInstalledModule方法。

    收起
    自动换行
    深色代码主题
    复制
    1. const moduleInfo: moduleInstallManager.InstalledModule = moduleInstallManager.getInstalledModule(moduleName);

创建按需加载的请求实例

  1. 导入moduleInstallManager模块及相关公共模块。

    收起
    自动换行
    深色代码主题
    复制
    1. import { moduleInstallManager } from '@kit.StoreKit';
    2. import type { common } from '@kit.AbilityKit';

  2. 构造参数。

    入参为当前应用的上下文context,只支持UIAbilityContextExtensionContext类型的上下文,其中UIAbilityContext类型的上下文是要校验当前应用是否在前台,如果不在前台,则会被拒绝调用。
    收起
    自动换行
    深色代码主题
    复制
    1. const context: common.UIAbilityContext | common.ExtensionContext = getContext(this) as common.UIAbilityContext;

  3. 调用createModuleInstallRequest方法,将步骤2中构造的参数依次传入模块中的createModuleInstallRequest方法。

    收起
    自动换行
    深色代码主题
    复制
    1. const myModuleInstallProvider: moduleInstallManager.ModuleInstallProvider = new moduleInstallManager.ModuleInstallProvider();
    2. const myModuleInstallRequest: moduleInstallManager.ModuleInstallRequest = myModuleInstallProvider.createModuleInstallRequest(context);

请求按需加载的接口

  1. 导入moduleInstallManager模块及相关公共模块。

    收起
    自动换行
    深色代码主题
    复制
    1. import type { common } from '@kit.AbilityKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. import { moduleInstallManager } from '@kit.StoreKit';

  2. 构造参数。

    入参为当前要按需加载的模块名。
    收起
    自动换行
    深色代码主题
    复制
    1. const moduleNameA: string = 'AModule';
    2. const moduleNameB: string = 'BModule';

  3. 调用ModuleInstallRequest中的addModule方法,将步骤2中构造的参数依次传入模块中的addModule方法。

    收起
    自动换行
    深色代码主题
    复制
    1. let myModuleInstallRequest: moduleInstallManager.ModuleInstallRequest;
    2. try {
    3. const myModuleInstallProvider: moduleInstallManager.ModuleInstallProvider = new moduleInstallManager.ModuleInstallProvider();
    4. const context: common.UIAbilityContext | common.ExtensionContext = getContext(this) as common.UIAbilityContext;
    5. myModuleInstallRequest = myModuleInstallProvider.createModuleInstallRequest(context);
    6. const aResult: moduleInstallManager.ReturnCode = myModuleInstallRequest.addModule(moduleNameA);
    7. const bResult: moduleInstallManager.ReturnCode = myModuleInstallRequest.addModule(moduleNameB);
    8. hilog.info(0, 'TAG', 'aResult:' + aResult + ' bResult:' + bResult);
    9. } catch (error) {
    10. hilog.error(0, 'TAG', `addModule onError.code is ${error.code}, message is ${error.message}`);
    11. }

  4. 调用fetchModules方法,将步骤三中的myModuleInstallRequest传入模块中的fetchModules方法。

    收起
    自动换行
    深色代码主题
    复制
    1. try {
    2. moduleInstallManager.fetchModules(myModuleInstallRequest)
    3. .then((data: moduleInstallManager.ModuleInstallSessionState) => {
    4. hilog.info(0, 'TAG', 'Succeeded in fetching Modules data.');
    5. })
    6. } catch (error) {
    7. hilog.error(0, 'TAG', `fetching Modules onError.code is ${error.code}, message is ${error.message}`);
    8. }

使用动态模块

假如应用A由entry.hap、AModulelib.hsp两个包组成,其中entry是基础包,AModulelib扩展是功能包(创建方式请参考应用程序包开发与使用)。通过应用市场下载安装只会下载安装entry包,在entry包里面可以通过fetchModules接口动态下载AModulelib包,并使用动态import技术调用AModulelib里的方法和组件。

AModulelib中主要实现如下:

  • 在动态模块AModulelib的module.json5中设置deliveryWithInstall为false,来标识当前AModulelib在用户主动安装应用A的时候不会一起下载安装。
    收起
    自动换行
    深色代码主题
    复制
    1. {
    2. "module": {
    3. "name": "AModulelib",
    4. "deliveryWithInstall": false
    5. }
    6. }
  • 在动态模块AModulelib中定义add方法和DateComponent组件。其中add方法用于计算加法,DateComponent用于显示文本。

    Calc.ets定义如下:

    收起
    自动换行
    深色代码主题
    复制
    1. export function add(a:number, b:number) {
    2. return a + b;
    3. }
    DateComponent.ets定义如下:
    收起
    自动换行
    深色代码主题
    复制
    1. @Component
    2. struct DateComponent {
    3. build() {
    4. Column() {
    5. Text('我是AModulelib中的组件')
    6. .margin(10);
    7. }
    8. .width(300).backgroundColor(Color.Yellow);
    9. }
    10. }
    11. @Builder
    12. export function showDateComponent() {
    13. DateComponent()
    14. }
  • 在AModulelib的AModulelib/Index.ets中导出add方法和showDateComponent方法。
    收起
    自动换行
    深色代码主题
    复制
    1. export { add } from './src/main/ets/utils/Calc';
    2. export { showDateComponent } from './src/main/ets/components/DateComponent';

entry中主要实现如下:

  • 在entry基础模块中,增加动态依赖配置。entry的oh-package.json5中使用dynamicDependencies来动态依赖AModulelib模块。
    收起
    自动换行
    深色代码主题
    复制
    1. {
    2. "dynamicDependencies": {
    3. "AModulelib": "file:../AModulelib"
    4. }
    5. }
  • 在entry中使用动态模块AModulelib模块里面的方法和组件。在调用AModulelib中的功能前需要判断AModulelib是否已经加载,未加载时请参考请求按需加载的接口完成加载。
    收起
    自动换行
    深色代码主题
    复制
    1. import { moduleInstallManager } from '@kit.StoreKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. import { BusinessError, Callback } from '@kit.BasicServicesKit';
    4. import { common } from '@kit.AbilityKit';
    5. import { promptAction } from '@kit.ArkUI';
    6. const TAG: string = 'TAG';
    7. @Entry
    8. @Component
    9. struct Index {
    10. @BuilderParam AModulelibComponent: Function;
    11. @State countTotal: number = 0;
    12. @State isShow: boolean = false;
    13. build() {
    14. Row() {
    15. Column() {
    16. Button(`调用增量模块中的add功能:3+6`)
    17. .onClick(() => {
    18. this.initAModulelib(() => {
    19. import('AModulelib').then((ns: ESObject) => {
    20. this.countTotal = ns.add(3, 6);
    21. }).catch((error: BusinessError) => {
    22. hilog.error(0, 'TAG', `add onError.code is ${error.code}, message is ${error.message}`);
    23. })
    24. })
    25. });
    26. Text('计算结果:' + this.countTotal)
    27. .margin(10);
    28. Button(`调用增量模块中的showDateComponent功能`)
    29. .onClick(() => {
    30. this.initAModulelib(() => {
    31. import('AModulelib').then((ns: ESObject) => {
    32. this.AModulelibComponent = ns.showDateComponent;
    33. this.isShow = true;
    34. }).catch((error: BusinessError) => {
    35. hilog.error(0, 'TAG', `showDateComponent onError.code is ${error.code}, message is ${error.message}`);
    36. })
    37. })
    38. }).margin({
    39. top: 10, bottom: 10
    40. });
    41. if (this.isShow) {
    42. this.AModulelibComponent()
    43. }
    44. }
    45. .width('100%')
    46. }
    47. .height('100%')
    48. }
    49. private showToastInfo(msg: string) {
    50. promptAction.showToast({
    51. message: msg,
    52. duration: 2000
    53. });
    54. }
    55. /**
    56. * 检查是否已加载AModulelib包
    57. *
    58. * @param successCallBack 回调
    59. */
    60. private initAModulelib(successCallBack: Callback<void>): void {
    61. try {
    62. const result: moduleInstallManager.InstalledModule = moduleInstallManager.getInstalledModule('AModulelib');
    63. if (result?.installStatus === moduleInstallManager.InstallStatus.INSTALLED) {
    64. hilog.info(0, TAG, 'AModulelib installed');
    65. successCallBack && successCallBack();
    66. } else {
    67. // AModulelib模块未安装, 需要调用fetchModules下载AModulelib模块。
    68. hilog.info(0, TAG, 'AModulelib not installed');
    69. this.fetchModule('AModulelib', successCallBack)
    70. }
    71. } catch (error) {
    72. hilog.error(0, 'TAG', `getInstalledModule onError.code is ${error.code}, message is ${error.message}`);
    73. }
    74. }
    75. /**
    76. * 添加监听事件
    77. *
    78. * @param successCallBack 回调
    79. */
    80. private onListenEvents(successCallBack: Callback<void>): void {
    81. const timeout = 3 * 60; //单位秒, 默认最大监听时间为30min(即30*60秒)
    82. moduleInstallManager.on('moduleInstallStatus', (data: moduleInstallManager.ModuleInstallSessionState) => {
    83. // 返回成功
    84. if (data.taskStatus === moduleInstallManager.TaskStatus.INSTALL_SUCCESSFUL) {
    85. successCallBack && successCallBack();
    86. this.showToastInfo('install success');
    87. }
    88. }, timeout)
    89. }
    90. /**
    91. * 加载指定包
    92. *
    93. * @param moduleName 需要加载的安装包名称
    94. * @param successCallBack 回调
    95. */
    96. private fetchModule(moduleName: string, successCallBack: Callback<void>) {
    97. try {
    98. hilog.info(0, TAG, 'handleFetchModules start');
    99. const context = getContext(this) as common.UIAbilityContext;
    100. const moduleInstallProvider: moduleInstallManager.ModuleInstallProvider =
    101. new moduleInstallManager.ModuleInstallProvider();
    102. const moduleInstallRequest: moduleInstallManager.ModuleInstallRequest =
    103. moduleInstallProvider.createModuleInstallRequest(context);
    104. if (!moduleInstallRequest) {
    105. hilog.warn(0, TAG, 'moduleInstallRequest is empty');
    106. return;
    107. }
    108. moduleInstallRequest.addModule(moduleName);
    109. moduleInstallManager.fetchModules(moduleInstallRequest)
    110. .then((data: moduleInstallManager.ModuleInstallSessionState) => {
    111. hilog.info(0, TAG, 'Succeeded in fetching Modules result.');
    112. if (data.code === moduleInstallManager.RequestErrorCode.SUCCESS) {
    113. this.onListenEvents(successCallBack)
    114. } else {
    115. hilog.info(0, TAG, 'fetchModules failure');
    116. }
    117. })
    118. .catch((error: BusinessError) => {
    119. hilog.error(0, 'TAG', `fetchModules onError.code is ${error.code}, message is ${error.message}`);
    120. })
    121. } catch (error) {
    122. hilog.error(0, 'TAG', `handleFetchModules onError.code is ${error.code}, message is ${error.message}`);
    123. }
    124. }
    125. }

运行结果效果图:

在 指南 中进行搜索
请输入您想要搜索的关键词