目录

On-Demand Feature Distribution (ArkTS)

Starting from version 26.0.0, a new API is added to allow users to pause download tasks.

Use Cases

As HarmonyOS apps continue to evolve, their features are becoming increasingly diverse. 80% of users tend to use just 20% of app features, while the remaining features are tailored to specific users. To prevent first-time app downloads from taking too long and using too much users' device space, AppGallery Kit offers on-demand distribution, which allows users to dynamically download enhanced features as needed.

Basic Concepts

On-demand distribution: An app is packaged into multiple installation packages, which contain all app code and static resources. The app downloaded from AppGallery contains only basic features. When a user needs to use enhanced features, they can download the corresponding installation packages from the server to their devices. (For details about app release, please refer to Releasing a HarmonyOS App.)

Service Process

  1. A user downloads the basic package of app A.

  2. The user requests enhanced features from the app.

  3. The app downloads the dynamic installation package through the API.

  4. The dynamic installation package is downloaded.

  5. The app notifies the user of the download result through the on API.

Constraints

API Description

On-demand feature distribution provides the following ArkTS APIs. For details, please refer to the API reference.

API Description
getInstalledModule(moduleName: string): InstalledModule Queries module installation information.
createModuleInstallRequest(context: common.UIAbilityContext | common.ExtensionContext): ModuleInstallRequest Creates an on-demand loading request object.
addModule(moduleName: string): ReturnCode Adds the name of the module to be loaded on demand.
fetchModules(moduleInstallRequest: ModuleInstallRequest): Promise<ModuleInstallSessionState> Requests on-demand loading and returns the result asynchronously.
cancelTask(taskId: string): ReturnCode Cancels a download task.
pauseTask(taskId: string): ReturnCode Pauses a download task.
showCellularDataConfirmation(context: common.UIAbilityContext | common.ExtensionContext, taskId: string): ReturnCode Displays a mobile data reminder pop-up.
on(type: 'moduleInstallStatus', callback: Callback<ModuleInstallSessionState>, timeout: number): void Listens for the progress of the current app download task.
off(type: 'moduleInstallStatus', callback?: Callback<ModuleInstallSessionState>): void Unregisters the listener for the current app download task progress.

Development Procedure

[h2]Obtaining Module Installation Information

  1. Import the moduleInstallManager module and related common modules.

    import { moduleInstallManager } from '@kit.AppGalleryKit';
  2. Construct an input parameter.

    The input parameter is the name of the module to be queried.

    const moduleName: string = 'AModulelib';
  3. Call the getInstalledModule API by passing the parameter constructed in step 2.

    const moduleInfo: moduleInstallManager.InstalledModule =
      moduleInstallManager.getInstalledModule(moduleName);

[h2]Creating an On-Demand Loading Request Object

  1. Import the moduleInstallManager module and related common modules.

    import { moduleInstallManager } from '@kit.AppGalleryKit';
    import type { common } from '@kit.AbilityKit';
  2. Construct an input parameter.

    The input parameter is the context of the current app. Only contexts of the UIAbilityContext and ExtensionContext types are supported. The context of the UIAbilityContext type is used to check whether the current app is running in the foreground. If the current app is not running in the foreground, the call will be rejected.

    const context: common.UIAbilityContext | common.ExtensionContext =
      this.getUIContext().getHostContext() as common.UIAbilityContext;
  3. Call the createModuleInstallRequest API by passing the parameter constructed in step 2.

    const moduleInstallProvider: moduleInstallManager.ModuleInstallProvider = new moduleInstallManager.ModuleInstallProvider();
    const moduleInstallRequest: moduleInstallManager.ModuleInstallRequest = moduleInstallProvider.createModuleInstallRequest(context);

[h2]Requesting On-Demand Loading

  1. Import the moduleInstallManager module and related common modules.

    import { moduleInstallManager } from '@kit.AppGalleryKit';
    import type { common } from '@kit.AbilityKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
  2. Construct an input parameter.

    The input parameter is the name of the module to be loaded on demand.

    const moduleName: string = 'AModulelib';
  3. Call the addModule API of ModuleInstallRequest by passing the parameter constructed in step 2.

    let moduleInstallRequest: moduleInstallManager.ModuleInstallRequest;
    try {
      // ...
      const context: common.UIAbilityContext | common.ExtensionContext =
        this.getUIContext().getHostContext() as common.UIAbilityContext;
      const moduleInstallProvider: moduleInstallManager.ModuleInstallProvider =
        new moduleInstallManager.ModuleInstallProvider();
      moduleInstallRequest = moduleInstallProvider.createModuleInstallRequest(context);
      // ...
      const retCode: moduleInstallManager.ReturnCode = moduleInstallRequest.addModule(moduleName);
      hilog.info(0, 'InstantDownload', `addModule result: ${JSON.stringify(retCode)}`);
    
      // ...
    } catch (error) {
      hilog.error(0, 'InstantDownload', `onError.code is ${error.code}, message is ${error.message}`);
    }
  4. Call the fetchModules method to request on-demand loading of a module.

    try {
      moduleInstallManager.fetchModules(moduleInstallRequest)
        .then((data: moduleInstallManager.ModuleInstallSessionState) => {
          hilog.info(0, 'InstantDownload', `fetchModule result: ${JSON.stringify(data)}`);
          // ...
        })
      // ...
    } catch (error) {
      hilog.error(0, 'InstantDownload', `fetching Modules onError.code is ${error.code}, message is ${error.message}`);
    }

[h2]Pausing a Download Task

  1. Import the moduleInstallManager module and related common modules.

    import { moduleInstallManager } from '@kit.AppGalleryKit';
    // ...
    import { hilog } from '@kit.PerformanceAnalysisKit';
  2. Construct an input parameter, which is the ID of the download task to be paused.

    // taskId is a field in ModuleInstallSessionState returned by fetchModules.
    let taskId: string = '********';
  3. When the network environment changes or device resources are insufficient, call the pauseTask API to pause the download task.

    try {
      // ...
      const rtnCode: moduleInstallManager.ReturnCode = moduleInstallManager.pauseTask(taskId);
      hilog.info(0, 'InstantDownload', `Succeeded in getting result: ${JSON.stringify(rtnCode)}`);
      // ...
    } catch (error) {
      hilog.error(0, 'InstantDownload', `pauseTask onError.code is ${error.code}, message is ${error.message}`);
      // ...
    }

[h2]Resuming a Download Task

After a download task is paused using pauseTask, you can call the fetchModules API to resume the download task from the point where it was paused.

import { moduleInstallManager } from '@kit.AppGalleryKit';
import type { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
// ...
@Component
struct ResumeTask {
  build() {
    Column() {
      Button('ResumeTask')
        .onClick(() => {
          try {
            const taskId: string = '********';
            // Pause a download task.
            const rtnCode: moduleInstallManager.ReturnCode = moduleInstallManager.pauseTask(taskId);
            hilog.info(0, 'InstantDownload', `Succeeded in getting result: ${JSON.stringify(rtnCode)}`);
            const myModuleInstallProvider: moduleInstallManager.ModuleInstallProvider =
              new moduleInstallManager.ModuleInstallProvider();
            const context: common.UIAbilityContext | common.ExtensionContext =
              this.getUIContext().getHostContext() as common.UIAbilityContext;
            // Create an on-demand loading request object.
            const myModuleInstallRequest: moduleInstallManager.ModuleInstallRequest =
              myModuleInstallProvider.createModuleInstallRequest(context);
            // Add the name of the module to be loaded on demand.
            myModuleInstallRequest.addModule('AModulelib');
            // Resume the download task.
            moduleInstallManager.fetchModules(myModuleInstallRequest)
              .then(() => {
                hilog.info(0, 'InstantDownload', 'Succeeded in fetching modules success data.');
              })
          } catch (error) {
            hilog.error(0, 'InstantDownload',
              `fetching modules onError.code is ${error.code}, message is ${error.message}`);
          }
        })
        .width('100%')
    }
    .margin(16)
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

[h2]Canceling a Download Task

  1. Import the moduleInstallManager module and related common modules.

    import { moduleInstallManager } from '@kit.AppGalleryKit';
    import type { common } from '@kit.AbilityKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
  2. Construct an input parameter, which is the ID of the download task to be canceled.

    // taskId is a field in ModuleInstallSessionState returned by fetchModules.
    let taskId: string = '********';
  3. Call the cancelTask API to cancel the download task.

    try {
      // ...
      const rtnCode: moduleInstallManager.ReturnCode = moduleInstallManager.cancelTask(taskId);
      hilog.info(0, 'InstantDownload', `Succeeded in getting result: ${JSON.stringify(rtnCode)}`);
      // ...
    } catch (error) {
      hilog.error(0, 'InstantDownload', `cancelTask onError.code is ${error.code}, message is ${error.message}`);
      // ...
    }

[h2]Using Dynamic Modules

Assume that app A consists of two packages: entry.hap and AModulelib.hsp. The entry package provides basic features, and the AModulelib package provides enhanced features. For details about how to create a package, please click here. AppGallery provides only the entry package for users to download and install, while the AModulelib package can be dynamically downloaded through the fetchModules API in the entry package. Methods and components in AModulelib are called using the dynamic import technology.

The implementation in AModulelib is as follows:

The implementation in entry is as follows:

[h2]Performing Debugging

On-demand feature distribution provides you with access to the debug function, allowing you to debug your app during the integration process without the need to release your app on AppGallery. Assume that app A consists of two packages: entry.hap and AModulelib.hsp. The entry package provides basic features, and the AModulelib package provides enhanced features. For details about how to create a package, please click here.

  1. Use the debug certificate to sign your app or atomic service. Compile locally to generate entry.hap and AModulelib.hsp. You can install the basic package either using hdc commands or directly through DevEco Studio.

    hdc install entry.hap
  2. Enable the developer debug mode: Go to Settings, tap the device model, tap the software version for seven consecutive times on the About screen, and tap Enable when the "Enable Developer options?" message is displayed.

  3. Access the device sandbox path and, within the app's encrypted data directory el2, create the cache/moduleinstall/<ModuleName> directory (where <ModuleName> is AModulelib here). Then, upload the module's debug package AModulelib.hsp to the corresponding module directory. (Ensure that the debug package file is granted the read and write permissions.)

  4. Install the module's debug package, cancel a download task, or resume a download task by referring to Creating an On-Demand Loading Request Object, Requesting On-Demand Loading, Canceling a Download Task, Resuming a Download Task, or Using Dynamic Modules, without the need to modify parameters. Once the installation is successful, the files in the corresponding module directory will be automatically deleted.