HarmonyOS HAP 包体积治理:拆包审计、资源收口与模块取舍
HarmonyOS HAP 包体积治理:拆包审计、资源收口与模块取舍
应用功能没有明显增加,发布包却在几个迭代后增长了十几 MiB。仅凭“最近加了很多图片”去猜,往往会漏掉重复资源、调试文件、多个架构的原生库,以及被多个 HAP 重复打入的 HAR 内容。包体积治理必须从产物出发:先记录同口径基线,再拆开安装包统计组成,最后把每一次收口动作与构建结果对应起来。
本文不提供“删除一切大文件”的激进清单,而是建立一套可回归的 HAP 审计流程。读者可以沿着代码、资源、原生库和模块依赖四条线定位增长来源,并在 HAR 与 HSP 之间做有依据的选择。

一、包体积的第一原则:比较必须同口径
Debug 与 Release、不同产品、不同设备类型、是否开启混淆或压缩,都会改变产物。两个文件都以 .hap 结尾,不代表它们可以直接比较。
建立基线时至少记录:
| 字段 | 示例含义 |
|---|---|
| 提交或版本 | 对应哪次构建 |
| 产品与 target | phone、tablet 或具体产品 |
| 构建类型 | Release / Debug |
| SDK 与 DevEco Studio | 构建工具口径 |
| HAP 名称 | entry 或 feature 模块 |
| 文件字节数 | 不只记录四舍五入后的 MiB |
| 解包后总字节数 | 用于判断压缩影响 |
| 最大目录或文件 | 快速定位变化 |
只有条件一致时,增量才有意义。首次治理不要先定“必须小于 20 MiB”,而应先回答当前包由什么组成、过去两次为何增长。
二、理解 APP、HAP、HAR 与 HSP 的关系
| 产物或模块 | 作用 | 体积治理关注点 |
|---|---|---|
| APP | 用于分发的应用包集合 | 包含哪些 HAP/HSP |
| HAP | 可安装和运行的能力模块 | 代码、资源、依赖是否重复 |
| HAR | 静态共享包 | 使用方构建时内容可能并入产物 |
| HSP | 动态共享包 | 公共代码资源可独立复用 |
HAR 接入简单,适合通用组件和库;但当同一份大型 HAR 同时被多个 HAP 使用时,需要检查其内容是否在多个产物中重复。HSP 可以承载应用内共享代码与资源,帮助减少重复,但并非“换成 HSP 一定更小”:它会改变模块边界、依赖方式和运行组织,必须结合启动、发布、兼容和维护成本评估。
三、从安装包组成图建立审计方向

治理时把内容分为四组:
- ArkTS 与编译产物:业务代码、三方库、调试残留;
- 资源文件:图片、字体、音视频、多语言和重复变体;
- 原生库:
.so、架构目录、符号和不需要的能力; - 模块依赖:HAR/HSP、Feature HAP 和跨模块重复。
每组都要有“定位证据、改动动作、回归验证”。只看 HAP 总大小,无法判断一次优化究竟删除了什么。
四、先用官方解包能力得到可审计目录
华为开发者文档提供 HAP 解包工具与相关说明。实际操作时,应使用与当前 SDK/工具链匹配的官方能力解包,不要把网上未经核对的命令直接用于发布产物。
得到解包目录后,再用 PowerShell 生成文件清单:
$Root = "D:\package-audit\entry-release-unpacked"
$Files = Get-ChildItem -LiteralPath $Root -Recurse -File |
ForEach-Object {
[PSCustomObject]@{
Path = $_.FullName.Substring($Root.Length)
Extension = if ($_.Extension) { $_.Extension.ToLowerInvariant() } else { "[none]" }
Bytes = $_.Length
MiB = [Math]::Round($_.Length / 1MB, 3)
}
}
$Files |
Sort-Object Bytes -Descending |
Select-Object -First 30 |
Format-Table Path, MiB -AutoSize
这个清单先回答“最大的 30 个文件是什么”。不要看到大文件就删除:地图、模型或启动资源可能本来就是核心能力,治理目标应是去除不必要、重复或错误打包的内容。
五、按扩展名汇总,判断增长来自哪一类
$ByType = $Files |
Group-Object Extension |
ForEach-Object {
$Total = ($_.Group | Measure-Object Bytes -Sum).Sum
[PSCustomObject]@{
Extension = $_.Name
Count = $_.Count
Bytes = $Total
MiB = [Math]::Round($Total / 1MB, 3)
}
} |
Sort-Object Bytes -Descending
$ByType | Format-Table Extension, Count, MiB -AutoSize
扩展名汇总能快速发现字体、PNG、WebP、音频或 .so 是否占主导,但它不能代替目录分析。没有扩展名的编译产物、容器文件也可能很大,因此还要按一级目录汇总。
$Files |
ForEach-Object {
$Relative = $_.Path.TrimStart("\")
$Top = ($Relative -split "\\")[0]
[PSCustomObject]@{
Top = $Top
Bytes = $_.Bytes
}
} |
Group-Object Top |
ForEach-Object {
[PSCustomObject]@{
Directory = $_.Name
MiB = [Math]::Round(
(($_.Group | Measure-Object Bytes -Sum).Sum) / 1MB,
3
)
}
} |
Sort-Object MiB -Descending
把两份统计保存为 CSV,后续迭代就能按类别比较,而不是只截一张总大小截图。
六、查重必须基于内容哈希,不要只看文件名
同一张图可能改了名称放在多个模块中,也可能同名但内容不同。使用 SHA-256 按内容分组:
$DuplicateGroups = Get-ChildItem -LiteralPath $Root -Recurse -File |
Get-FileHash -Algorithm SHA256 |
Group-Object Hash |
Where-Object { $_.Count -gt 1 }
foreach ($Group in $DuplicateGroups) {
$Paths = $Group.Group.Path
$Size = (Get-Item -LiteralPath $Paths[0]).Length
[PSCustomObject]@{
Copies = $Group.Count
OneFileMiB = [Math]::Round($Size / 1MB, 3)
WasteMiB = [Math]::Round(
($Size * ($Group.Count - 1)) / 1MB,
3
)
Paths = $Paths -join " | "
}
}
“可节省空间”这里只是理论估算。资源是否能合并取决于模块访问边界、主题差异、设备限定和运行时加载方式。治理记录应注明最终采取的是删除、共用、按产品拆分,还是保留并说明原因。
七、资源收口要从使用场景开始
资源类常见问题包括:
- 设计稿导出的 2 倍、3 倍图同时打入,但实际只用一个版本;
- 同一字体包含多种字重,页面只使用其中一两种;
- 示例视频、占位图和测试 JSON 进入 Release;
- phone、tablet、wearable 的专属资源进入同一个目标产物;
- 多语言文件复制了大段不需要的内容;
- 可由矢量或绘制实现的简单背景被保存为大位图。
为每个候选资源建立审计行:
资源路径:
解包后字节数:
被哪些页面引用:
适用设备/产品:
是否存在内容重复:
替代方案:
删除后的功能回归点:
没有引用证据时,不要根据文件名猜测“没用”。动态资源名、反射式访问和 Web 内容可能不容易被普通文本搜索发现,需要结合运行路径验证。
八、图片优化不是统一降低质量
图片治理应先分类:
| 类型 | 优先动作 | 验证 |
|---|---|---|
| 图标和简单图形 | 优先矢量或资源复用 | 多尺寸边缘是否清晰 |
| 照片 | 选择适合的有损格式和尺寸 | 色带、细节和加载耗时 |
| 透明大图 | 检查是否真的需要全尺寸透明通道 | 透明边缘与内存 |
| 启动关键图 | 兼顾体积与解码成本 | 冷启动首帧 |
| 多设备背景 | 按产品或 target 提供 | 各设备不缺图 |
不要把所有 PNG 批量改成另一种格式后直接提交。格式支持、透明度、解码成本和视觉质量都要在目标设备检查;某些图片文件变小,却可能增加运行时解码压力。
九、字体和音视频通常是单文件大户
字体子集化可以显著减小包体积,但前提是明确字符范围。中文应用若只保留当前页面字符,用户输入、服务端文案或多语言内容可能显示为缺字。更稳的做法是:
- 统计实际字重和语言范围;
- 去除完全未使用的字体文件;
- 对可控展示文本评估子集;
- 在服务端动态文案、数字、符号和多语言下回归;
- 保留许可信息并遵守字体授权。
音视频同样需要先确认是否必须随包交付。可联网下载的内容可以迁移到按需资源,但要评估首用等待、离线能力、网络失败和审核要求,不能为了减小包体积破坏核心离线场景。
十、原生库先盘点架构和来源
从解包目录列出原生库:
$NativeLibraries = Get-ChildItem -LiteralPath $Root -Recurse -File |
Where-Object { $_.Extension -eq ".so" } |
Select-Object FullName, Length |
Sort-Object Length -Descending
$NativeLibraries |
ForEach-Object {
[PSCustomObject]@{
Path = $_.FullName.Substring($Root.Length)
MiB = [Math]::Round($_.Length / 1MB, 3)
}
} |
Format-Table Path, MiB -AutoSize
每个 .so 应能回答:来自哪个依赖、支持哪个架构、哪些功能调用、Release 是否需要、是否包含不应随包发布的符号或样例能力。不能因为某个架构目录体积大就直接删除,设备兼容范围和商店发布策略必须先确认。
十一、三方依赖治理要看“能力是否重复”
两个依赖可能分别提供 JSON、网络、图片或压缩能力,业务只用很小一部分。依赖审计表可以这样维护:
| 依赖 | 引入模块 | 实际调用能力 | 是否与现有能力重叠 | 去除验证 |
|---|---|---|---|---|
| 库 A | entry | 图片变换 | 与系统能力部分重叠 | 视觉回归、性能 |
| 库 B | feature | 压缩 | 无重叠 | 解压兼容 |
| 库 C | 多个 HAP | 通用模型 | 可能重复打包 | 模块产物对比 |
不要为“以后可能用”长期保留大型依赖。删除依赖时同时检查 oh-package.json5、锁文件、import、构建配置和原生产物,避免只删声明却留下生成文件。
十二、HAR 与 HSP 的选择要用重复量证明

当多个 HAP 同时使用大型公共 HAR 时,先分别解包并计算重复文件或相同内容的总量。只有重复规模足够、模块边界稳定,才值得评估迁移 HSP。
| 决策问题 | 偏向 HAR | 偏向 HSP |
|---|---|---|
| 只有一个使用方 | 是 | 通常没有共享收益 |
| 库很小且稳定 | 是 | 迁移收益有限 |
| 多个 HAP 重复大型代码资源 | 需警惕 | 可评估共享 |
| 需要独立模块边界 | 视项目而定 | 更匹配共享组织 |
| 团队尚未验证启动和发布影响 | 先保持 | 不应只为数字迁移 |
迁移前后必须比较整个 APP,而不是只看某一个 HAP 变小。公共内容转移到 HSP 后,单 HAP 可能下降,但总安装包和运行表现才是最终指标。
十三、按产品和设备收口资源
多设备应用不应让每个目标携带全部设备专属资源。模块设计和构建配置应按实际产品拆分入口、页面和资源目录。具体 JSON5 字段会随工程结构和 SDK 演进,配置前应以当前项目 build-profile.json5、模块 module.json5 和官方多设备文档为准。
可以先建立资源归属清单:
{
"phone": [
"compact-navigation",
"phone-onboarding"
],
"tablet": [
"two-pane-background",
"keyboard-shortcuts"
],
"shared": [
"brand-icons",
"core-localization"
]
}
这不是构建配置,而是审计输入。它迫使团队说明每个资源为何属于共享或专属,再据此修改真实 target 与资源目录配置。
十四、建立基线文件,让体积回归进入评审
把每次 Release 产物统计保存成 JSON:
{
"version": "2.6.0",
"product": "default",
"buildMode": "release",
"hapBytes": 28641280,
"unpackedBytes": 51783240,
"largestGroup": "resources",
"largestFileBytes": 6241580
}
使用简单脚本比较总量和增量:
param(
[string]$BaselinePath,
[string]$CurrentPath,
[long]$AllowedGrowthBytes = 1048576
)
$Baseline = Get-Content -LiteralPath $BaselinePath -Raw |
ConvertFrom-Json
$Current = Get-Content -LiteralPath $CurrentPath -Raw |
ConvertFrom-Json
if ($Baseline.product -ne $Current.product -or
$Baseline.buildMode -ne $Current.buildMode) {
throw "构建口径不一致,禁止比较"
}
$Growth = [long]$Current.hapBytes - [long]$Baseline.hapBytes
Write-Host ("HAP growth bytes: " + $Growth)
if ($Growth -gt $AllowedGrowthBytes) {
throw "包体积增长超过预算,请附拆包明细"
}
预算不是禁止增长。新增核心能力可以合理增加体积,但提交者要说明增长来源、是否有替代方案、对下载与存储的影响,并更新认可后的基线。
十五、优化动作要逐项构建,避免无法归因
一次提交同时改图片格式、删除依赖、迁移 HSP、裁剪字体,最终即使包变小,也很难知道各动作贡献和风险。建议按下面顺序迭代:
- 保存原始 Release 包与解包清单;
- 删除明确无引用的样例和调试资源,重新构建;
- 合并内容完全相同的资源,重新构建;
- 调整图片、字体和音视频,逐类回归;
- 清理未使用依赖和原生库;
- 最后评估模块级 HAR/HSP 改造;
- 每一步记录 HAP、APP 和解包后大小。
这样出现视觉、启动或兼容回归时,可以快速定位到具体治理动作。
十六、常见误区与修复方向
| 误区 | 为什么不可靠 | 正确方向 |
|---|---|---|
| 只看 HAP 文件大小 | 看不到 APP 总体和内部组成 | 同时记录 APP、HAP、解包清单 |
| 大文件一律删除 | 可能是核心资源 | 先查引用和设备归属 |
| 所有图片统一降质 | 视觉和解码成本不同 | 按资源类型处理 |
| HAR 全部迁移 HSP | 模块复杂度和运行成本被忽略 | 用多 HAP 重复量证明收益 |
| 只在 Debug 对比 | 发布口径不同 | 固定 Release 产品构建 |
| 依赖声明删了就算完成 | 产物可能仍有残留 | 重建后拆包复核 |
| 单 HAP 变小就宣布成功 | 内容可能转移到其他包 | 比较完整 APP |
十七、交付前验收清单
- 基线与当前包使用相同产品、target、构建类型和工具链。
- 已保存 HAP/APP 文件字节数与解包后清单。
- 最大文件、扩展名汇总和一级目录汇总均已检查。
- 重复资源按内容哈希定位,而不是只按文件名。
- 每个删除或合并动作都有引用证据和功能回归项。
- 字体子集覆盖动态文案、数字、符号和多语言。
- 原生库的来源、架构和调用能力均可追溯。
- 多 HAP 公共内容的重复规模已经量化。
- HAR/HSP 决策比较的是完整 APP 与运行影响。
- 包体积增长预算进入 Release 构建检查。
- 优化后完成安装、启动、资源显示、离线和升级验证。
十八、用体积变更报告记录收益与代价
治理提交不能只写“包体积减小 5 MiB”。评审者需要知道减少来自哪里,是否移动到了其他 HAP/HSP,以及功能上付出了什么代价。建议每次提交附一份同口径报告:
构建口径:
- product / target:
- buildMode:
- SDK / DevEco Studio:
- 基线提交:
- 当前提交:
体积结果:
- APP:基线字节 -> 当前字节
- entry HAP:基线字节 -> 当前字节
- feature HAP:基线字节 -> 当前字节
- HSP:基线字节 -> 当前字节
- 解包总量:基线字节 -> 当前字节
变更归因:
- 删除未使用资源:
- 合并重复资源:
- 图片或字体调整:
- 依赖与原生库调整:
- 模块边界调整:
回归证据:
- 安装与升级:
- 冷启动:
- 多设备资源:
- 离线场景:
- 视觉与字体:
报告中的数字应来自保留的产物与脚本输出,而不是手工估算。内容从 HAR 移到 HSP 时,要同时填写所有相关包;图片改为按需下载时,还要记录首用等待、失败兜底和离线能力变化。
如果总 APP 体积几乎不变,但单个 HAP 显著变小,应如实说明这是模块重分布,不应写成整体节省。反过来,包体积下降但冷启动或首屏网络成本明显增加,也需要由产品与工程共同判断是否值得。
总结
HAP 包体积治理不是一次清理活动,而是产物审计能力。先固定 Release 比较口径,再用官方解包能力获得文件清单;从资源、原生库、代码依赖和模块组织四个方向定位大项,用哈希证明重复,用构建结果证明收益。HAR 与 HSP 的取舍也应建立在完整 APP 的重复量和运行回归上。把基线、预算和拆包明细纳入每次发布,体积增长才能从“到上架前才发现”变成日常可控制的工程指标。
参考资料
解包工具文档用于获取可审计产物,模块化设计与 HAR/HSP 文档用于判断共享内容的组织方式,术语文档用于统一 APP、HAP、HAR 与 HSP 的含义。具体构建配置应以项目当前 SDK、模块类型和官方最新说明为准。
更多推荐




所有评论(0)