Administrator
发布于 2026-09-03 / 8,915 阅读
0
0

鸿蒙包管理机制:HAP/HSP、安装卸载与版本控制

鸿蒙百篇系列第 68 篇 · 中级

第 20 篇讲上架流程是从"运维"角度讲的。这一篇我们钻进底层"包管理子系统",把 HAP / HSP / APP 的实质、签名链、版本号、安装卸载原理以及 bm 工具搞清楚。理解这一层之后,遇到"包安装失败"、“版本冲突”、"动态下发模块"这类问题就有数了。

一、HAP 是鸿蒙应用的最小装载单位

一个鸿蒙应用在不同阶段看到的是不同的"装载体":

名字

形态

用途

HAP(Harmony Ability Package)

静态包

装载 UIAbility / 资源 / 模块

HSP(Harmony Shared Package)

共享包

多 HAP 间共享代码/资源

APP

一组 HAP + HSP

上架应用整体

它们的物理结构(简化):

com.example.app.app                  # 顶层目录
├── pack.info                        # 整体信息
├── signature.p7s                    # 整体签名
├── entry.hap                        # 主模块
├── feature.hap                      # 业务特性包
└── shared_library.hsp               # 共享包

每个 HAP 里又有:

hap/
├── pack.info                        # HAP 名称、version
├── resources                        # 资源索引
├── module.json5                     # 模块配置
├── code.abc / code.deflate          # 字节码
└── libs/                            # 第三方 .so

HSP 的出现是为了打破"每个 HAP 重复装一份相同代码"的问题。多个 HAP 共享同一份 HSP,安装体积能减半,启动也更快。

二、HAP 的两种 mode

module.json5 里 deliveryWithInstall 字段决定 HAP 是否随主包安装:

Mode

含义

deliveryWithInstall: true

跟主包一起装

deliveryWithInstall: false

按需下载

按需下载的 HAP 在 requestPermissions 之外,会再注册一个 com.example.app.${feature} 的 feature 标识,用于按需 lazy load。

应用启动时按需动态加载 Feature HAP:

import { hilog } from '@kit.PerformanceAnalysisKit';

const moduleName = 'feature_news';

aboutToAppear() {
  const module = require(moduleName);
  this.newsPage = module.default;
}

// 或者用 initializeAbility / loadModule API

按需下载是大型应用瘦身最直接的手段。微信、抖音这种应用都是 entry + 多个 feature module 组合。

三、bundle 包结构体:BundleInfo 与 ApplicationInfo

运行时通过 bundleManager 模块查询包信息:

import bundleManager from '@ohos.bundle.bundleManager';

const bundleInfo = await bundleManager.getBundleInfo({
  bundleName: 'com.example.app',
  bundleFlag: bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE |
              bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_ABILITY |
              bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SKILL |
              bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO
});

console.info(`versionName: ${bundleInfo.versionName}`);
console.info(`versionCode: ${bundleInfo.versionCode}`);
console.info(`签名: ${bundleInfo.signatureInfo?.appId}`);
console.info(`HAP modules:`);
for (const m of bundleInfo.hapModulesInfo) {
  console.info(`  - ${m.name} (${m.hapPath})`);
}

关键字段:

字段

含义

versionCode

内部版本号,必须单调递增

versionName

用户可见版本号

minCompatibleVersionCode

最低兼容版本

targetVersion

目标 API 级别

appInfo.bundleType

APP 或 ATOMIC_SERVICE

signatureInfo.appId

通过签名算出的 appId,每个 app 唯一

versionCode 是鸿蒙做"版本比对"的依据。升级时必须大于旧版本,否则安装直接被拒。

四、安装和卸载:bm 命令与 API

bm 是鸿蒙提供的 CLI 工具,可以脱离 IDE 直接装包:

# 进入设备 shell
hdc shell

# 安装 HAP
bm install -p /data/app/entry.hap

# 覆盖安装
bm install -p /data/app/entry.hap -r

# 安装 HSP 共享包
bm install -s /data/app/shared.hsp

# 安装多包(一个 entry + 多个 feature)
bm install -p /data/app/entry.hap -s /data/app/lib1.hsp /data/app/lib2.hsp

# 等到一定时间再装(给 UI 闭环比如登录)
bm install -p /data/app/entry.hap -w 10

# 卸载
bm uninstall -n com.example.app

# 卸载但保留数据
bm uninstall -n com.example.app -k

# 卸载 HSP
bm uninstall -n com.example.shared -s

# 查询
bm dump -a           # 全部包
bm dump -n com.example.app   # 指定包
bm dump -s shortcutInfo -n com.example.app  # 快捷方式
bm clean -n com.example.app  # 清理缓存

注意 -p 装包路径应用 sandbox 不可写,要先用 hdc file recv 把文件传到设备可访问目录。

五、应用层面安装:startAbility

如果你想在自己的应用里启动"安装"流程,调起系统安装器:

import common from '@ohos.app.ability.common';
import { BusinessError } from '@kit.BasicServicesKit';

async function installBundle(context: common.UIAbilityContext, hapPath: string) {
  // ohos.want.action.INSTALL_BUNDLE
  const want = {
    action: 'ohos.want.action.INSTALL_BUNDLE',
    parameters: {
      'ohos.extra.param.key.bundleFilePath': hapPath
    }
  };

  try {
    await context.startAbility(want);
  } catch (e) {
    const err = e as BusinessError;
    console.error(`启动安装器失败: code=${err.code}, msg=${err.message}`);
  }
}

需要 ohos.permission.INSTALL_BUNDLE 权限。这个 API 适合"在线下载更新包后即时安装"的场景,例如 OTA。

六、签名校验:HAP 的防篡改机制

每个 HAP 都有签名 signature.p7s:

  • 由发布者私钥签名

  • 内容是 HAP 文件 hash

  • AGC 上的发布证书链可校验

import bundleManager from '@ohos.bundle.bundleManager';

async function verifySignature(bundleName: string, expectedAppId: string) {
  const info = await bundleManager.getBundleInfo({
    bundleName,
    bundleFlag: bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO
  });

  if (info.signatureInfo?.appId !== expectedAppId) {
    return false;
  }
  return true;
}

签名应用面很广:

  1. 应用市场校验:每个上架包都校验 appId 与开发者实名证书是否一致

  2. 运行时校验:API 12 提供"动态加载模块必须经过签名校验",第三方插件要传入正确的 bundleInfo

  3. 跨设备调用:分布式调用通过签名算 appId 决定权限

  4. 调试签名 / 发布签名分离:开发期用调试证书签名方便上架,但发布版必须用 AGC 证书

七、版本号与升级

versionCode 是 int32 数字,定义在 AppScope 里:

{
  "app": {
    "bundleName": "com.example.app",
    "versionCode": 1000210,
    "versionName": "1.0.2-rc10"
  }
}

升级规则:

行为

versionCode 关系

风险

普通升级

new > old

安全

同 version

new = old

会被 bm install -r 默认行为覆盖

降级

new < old

安装会被拒

设计 version 时的常用做法:

主版本号:兼容到 旧 3 个大版本
版本号:1000
次版本号:100
补丁版本号:1
最终: 1.2.10 → versionCode: 102110

八、应用类型与安装路径

app.bundleType 控制应用能否"原子化":

类型

安装位置

卸载

备注

APP

app/ 目录

完整卸载

普通应用

ATOMIC_SERVICE

app/cache/

只卸载当前实例

卡片服务

元服务的安装路径特殊,卸载时仅清理 user 级沙箱数据,共享代码可在另一处挂载。这一层是元服务"轻量"的基础。

九、几个工程坑

  1. HSP 升级冲突。HSP 是共享包,多个 HAP 引用同一份 HSP。如果某个 HAP 升级引入新版本 HSP,老版本同时拉起会失败。设计时 HSP 版本要和 HAP 一起升级

  2. 按需下载失败兜底。网络差时按需下载失败,进入应用要提供"重试"路径

  3. appId 与 bundleName 不同。每个应用有唯一 bundleName,签名后产生唯一 appId,前者是声明性,后者是计算性。混淆这两个导致权限链失败

  4. bundle install -w 时长:5-600 秒范围,默认 5 秒。下载更新包场景推荐 30+

  5. 自动更新后状态恢复。升级后 want.parameters 里的 deepLink 可能失效,要做路由兼容

  6. 签名工具链鸿蒙与 Android 不通用。OpenHarmony 签名工具跟 APK 签名不同,老工具失效。开发者从 Android 过来第一道坎

十、查询能力:ApplicationInfo / HapModuleInfo / AbilityInfo

const data = await bundleManager.getBundleInfo({
  bundleName: 'com.example.app',
  bundleFlag: bundleManager.BundleFlag.GET_BUNDLE_INFO_ALL
});

// 全量信息
const app = data.appInfo;        // ApplicationInfo
for (const m of data.hapModulesInfo) {
  const module = m;             // HapModuleInfo
  for (const a of m.abilityInfos) {
    const ability = a;          // AbilityInfo
  }
}

实战中几个用法:

// 1. 找到所有合法的 launchType='MAIN' 的 Ability
const mainAbility = data.hapModulesInfo
  .flatMap(m => m.abilityInfos ?? [])
  .find(a => a.launchType === bundleManager.LaunchType.SINGLETON);

// 2. 判断应用是否被卸载
const flags = data.appInfo.flags;
const isUninstalled = (flags & 0x1000) !== 0;

// 3. 给出所有 HAP 的 codePath
const codePaths = data.hapModulesInfo.map(m => m.codePath ?? m.hapPath);

十一、和前几篇的衔接

  • 第 20 篇讲上架流程,本篇是底层细节

  • 第 35 篇动态化,提到的卡片数据下发其实是基于 HAP/HSP 安装

  • 第 36 篇小程序整合,跨平台资源调度也用 bm 命令

十二、小结

包管理是鸿蒙"操作系统级功能"里做得很稳的一块。理解 HAP 物理结构、HSP 共享、按需下载、bm CLI 工具、bundleManager 查询,能让你在大型应用工程里少踩很多坑。下次看到 bm install -r 报错版本号不递增,能立刻知道问题在哪。

下一篇进入系统服务框架(SAMgr),看 SA 怎么注册、怎么被发现、跨设备怎么调用。


评论