HarmonyOS鸿蒙Next开发工坊是如何实现插件化的?

HarmonyOS鸿蒙Next开发工坊是如何实现插件化的? 开发样例里中的程序,可以下载后直接运行。是怎么实现的?


更多关于HarmonyOS鸿蒙Next开发工坊是如何实现插件化的?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

厉害了,源码都找到了👍,

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

cke_259.png

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

Demo实现:

import type { common, StartOptions } from '@kit.AbilityKit';
import { display } from '@kit.ArkUI';
import { BusinessError, emitter } from '@kit.BasicServicesKit';
import { moduleInstallManager } from '@kit.StoreKit';
import Logger from './Logger';

const DOWNLOAD_TIMEOUT_LIMIT: number = 1800;
const TAG: string = '[DynamicInstallManager]';

export class DynamicInstallManager {
  private static aVAbilityList: string[] = [
    'KnocksharesampleAbility',
    'VideocastsampleAbility',
    'AudiointeractionsampleAbility',
  ];

  public static getModuleStatus(moduleName: string): moduleInstallManager.InstallStatus {
    try {
      const result: moduleInstallManager.InstalledModule = moduleInstallManager.getInstalledModule(moduleName);
      Logger.info(TAG, `getModuleStatus moduleName: ${result.moduleName}, installStatus: ${result.installStatus}`);
      return result.installStatus;
    } catch {
      Logger.error(TAG, `GetModuleStatus getInstalledModule failed.`);
      return moduleInstallManager.InstallStatus.NOT_INSTALLED;
    }
  }

  public static fetchModule(context: common.UIAbilityContext,
    moduleName: string): Promise<moduleInstallManager.ModuleInstallSessionState> {
    return new Promise((resolve: (value: moduleInstallManager.ModuleInstallSessionState) => void,
      reject: (reason?: object) => void) => {
      try {
        Logger.info(TAG, `fetchModule moduleName: ${moduleName}`);
        const myModuleInstallProvider: moduleInstallManager.ModuleInstallProvider =
          new moduleInstallManager.ModuleInstallProvider();
        const moduleInstallRequest: moduleInstallManager.ModuleInstallRequest =
          myModuleInstallProvider.createModuleInstallRequest(context);
        moduleInstallRequest.addModule(moduleName);
        moduleInstallManager.fetchModules(moduleInstallRequest)
          .then((data: moduleInstallManager.ModuleInstallSessionState) => {
            Logger.debug(TAG, `fetchModule success, result: ${JSON.stringify(data)}`);
            resolve(data);
          });
      } catch (error) {
        const err: BusinessError = error as BusinessError;
        Logger.error(TAG, `request installing module failed, error: ${err.code} ${err.message}`);
        reject(error);
      }
    });
  }

  public static cancelDownloadTask(taskId?: string): void {
    try {
      const rtnCode: moduleInstallManager.ReturnCode = moduleInstallManager.cancelTask(taskId);
      Logger.info(TAG, `Succeeded in getting result: ${rtnCode}`);
    } catch (error) {
      Logger.error(TAG, `cancelTask error code is ${error.code}, message is ${error.message}`);
    }
  }

  public static subscribeDownloadProgress(): void {
    try {
      moduleInstallManager.on('moduleInstallStatus', (downloadData: moduleInstallManager.ModuleInstallSessionState) => {
        Logger.info(TAG,
          `subscribeDownloadProgress downloadsize: ${downloadData.downloadedSize}, totalsize: ${downloadData.totalSize}`);
        const eventData: emitter.EventData = {
          data: {
            'taskStatus': downloadData.taskStatus,
            'downloadedSize': downloadData.downloadedSize,
            'totalSize': downloadData.totalSize
          }
        };
        emitter.emit(CommonConstants.DYNAMIC_INSTALL_EVENT, eventData);
      }, DOWNLOAD_TIMEOUT_LIMIT);
      Logger.info(TAG, 'subscribe download progress success');
    } catch (error) {
      Logger.error(TAG, `subscribeDownloadProgress failed, error: ${error.code}, ${error.message}`);
    }
  }

  public static unsubscribeDownloadProgress(): void {
    try {
      moduleInstallManager.off('moduleInstallStatus');
      Logger.info(TAG, 'unsubscribe download progress success');
    } catch (error) {
      Logger.error(TAG, `onListening error code is ${error.code}, message is ${error.message}`);
    }
  }

  public static loadModule(context: common.UIAbilityContext, moduleAbility: string): Promise<void> {
    return new Promise((resolve: (value: void) => void, reject: (reason?: BusinessError) => void) => {
      try {
        const isAVAbility: boolean = DynamicInstallManager.aVAbilityList.includes(moduleAbility);
        const isSameAbility: boolean = (moduleAbility === AppStorage.get('AVAbilityModule'));
        const aVAbilityContext: common.UIAbilityContext =
          AppStorage.get<common.UIAbilityContext>('AVAbilityContext') as common.UIAbilityContext;
        if (isAVAbility && !isSameAbility && aVAbilityContext) {
          aVAbilityContext.terminateSelf();
          AppStorage.delete('AVAbilityContext');
        }
        const option: StartOptions = DynamicInstallManager.setStartAbilityProperty();
        context.startAbility({ bundleName: context.abilityInfo.bundleName, abilityName: moduleAbility }, option)
          .then(() => {
            if (isAVAbility) {
              AppStorage.setOrCreate('AVAbilityModule', moduleAbility);
            }
            Logger.info(TAG, `start ${moduleAbility} success}`);
            resolve();
          })
          .catch((error: BusinessError) => {
            Logger.error(TAG,
              `start ${moduleAbility} failed, error code is ${error.code}, message is ${error.message}`);
            reject(error);
          });
      } catch (error) {
        const err: BusinessError = error as BusinessError;
        Logger.error(TAG, `startAbility failed, error code is ${err.code}, message is ${err.message}`);
      }
    });
  }

  public static setStartAbilityProperty(): StartOptions {
    try {
      const uiContext: UIContext = AppStorage.get<UIContext>(StorageKey.UI_CONTEXT) as UIContext;
      const displayData = display.getDefaultDisplaySync();
      const windowWidth = displayData.availableWidth * CommonConstants.WINDOW_RATIO;
      const windowHeight = displayData.availableHeight * CommonConstants.WINDOW_RATIO;
      const windowLeft = (displayData.availableWidth - windowWidth) / 2.0;
      const windowTop = (displayData.availableHeight - windowHeight) / 2.0;
      const option: StartOptions = {
        minWindowWidth: Math.min(uiContext.px2vp(windowWidth), CommonConstants.MIN_WINDOW_WIDTH),
        minWindowHeight: Math.min(uiContext.px2vp(windowHeight), CommonConstants.MIN_WINDOW_HEIGHT),
        windowLeft: windowLeft,
        windowTop: windowTop,
        windowWidth: windowWidth,
        windowHeight: windowHeight,
      };
      return option;
    } catch {
      Logger.error(TAG, `SetStartAbilityProperty getDefaultDisplaySync failed.`);
      return {};
    }
  }
}

HarmonyOS Next的插件化机制基于ArkTS语言和Stage模型实现。系统通过动态加载HAP(Harmony Ability Package)文件实现插件功能,支持按需加载和独立更新。每个插件作为独立模块运行在沙箱环境中,通过ExtensionAbility机制与主应用通信。系统管理插件的生命周期和资源隔离,确保安全性和稳定性。

HarmonyOS Next的插件化能力主要通过ArkTS的静态共享包(Static Shared Package)动态共享包(Dynamic Shared Package) 实现,支持代码和资源的灵活分发与按需加载。

在开发工坊样例中,您下载后能直接运行,是因为样例通常以HAP(Harmony Ability Package) 形式提供,它包含了运行所需的代码、资源和配置文件。具体实现方式如下:

  1. 静态共享包(Static Shared Package):包含ArkTS代码、C++库和资源文件,在应用构建时直接集成到HAP中。您下载的样例已内置所需依赖,因此无需额外配置即可运行。

  2. 动态共享包(Dynamic Shared Package):支持在运行时按需加载,包含代码和资源,适用于功能模块的延迟加载或独立更新。

  3. 工程结构:样例工程通常采用标准的HarmonyOS应用结构,包含entry(主模块)和可选的featurelibrary模块。构建后生成HAP,可直接在真机或模拟器上安装运行。

  4. 工具链支持:DevEco Studio提供完整的开发、编译和调试工具链,确保样例工程能一键编译并打包为可部署的HAP。

因此,您下载的样例本质是一个已配置完备的HarmonyOS应用包(HAP),所有插件化依赖已在构建时处理完毕,实现了开箱即用的体验。

回到顶部