HarmonyOS 包体优化与构建配置实战:从体积分析到分包治理
为什么要做包体优化
应用包体积直接影响三个关键指标:
- 下载转化率:包体每增加 6MB,下载转化率下降约 1%
- 更新意愿:大包更新用户更容易放弃,尤其是流量环境下
- 应用商店排名:部分应用市场将包体积作为推荐权重因素
HarmonyOS 的 HAP/APP 包结构与 Android APK 有相似之处,但也有独特的优化空间——本文从体积分析、资源压缩、代码优化到分包策略,给出完整的工程化方案。
HAP 包结构与体积分析
HAP 包基本结构
entry.hap
├── ets/ # ArkTS 编译产物
├── resources/ # 资源文件
│ ├── base/
│ │ ├── element/ # 字符串、颜色等
│ │ ├── media/ # 图片、音频、视频
│ │ └── profile/ # 配置文件
│ ├── rawfile/ # 原始文件(不压缩)
├── libs/ # 原生库(so)
├── module.json # 模块配置
└── pack.info # 打包信息
体积分析命令
# 解压 HAP 查看结构
unzip entry.hap -d hap_extracted
# 查看各目录大小
du -sh hap_extracted/*
# 详细分析(递归显示前 20 大文件)
du -ah hap_extracted | sort -rh | head -20
常见占比:
- resources/base/media:40%-60%(图片、动画)
- ets:20%-30%(代码)
- libs:10%-20%(原生库)
- rawfile:0%-10%(不压缩资源)
资源优化
图片压缩与格式选择
// build-profile.json5
{
"apiType": "stageMode",
"buildOption": {
"arkOptions": {
"runtimeOnly": false
}
},
"targets": [
{
"name": "default",
"runtimeOS": "HarmonyOS",
"buildOption": {
"compileMode": "esmodule",
// 启用资源压缩
"enableObfuscation": true,
"enableMinification": true
}
}
]
}
图片格式选型:
| 格式 | 适用场景 | 压缩率 | 透明通道 |
|---|---|---|---|
| WebP | 通用位图 | 30%-50% 优于 PNG | 支持 |
| PNG | 需要无损透明 | 基准 | 支持 |
| JPG | 照片、渐变 | 比 PNG 小 50%-80% | 不支持 |
| SVG | 图标、简单图形 | 极小 | 支持 |
批量转换 WebP:
# 安装 cwebp(macOS)
brew install webp
# 批量转换
for img in resources/base/media/*.png; do
cwebp -q 85 "$img" -o "${img%.png}.webp"
done
移除未使用资源
// 启用资源收缩(build-profile.json5)
{
"buildOption": {
"shrinkResources": true // 移除未引用资源
}
}
手动排查未使用资源:
# 搜索所有资源引用
grep -r "R.media" ets/ > used_resources.txt
grep -r "\$r('app.media" ets/ >> used_resources.txt
# 对比 resources/base/media 中的文件,移除未引用的
压缩音频与视频
# 音频转 Opus(比 MP3 小 20%-40%)
ffmpeg -i input.mp3 -c:a libopus -b:a 64k output.opus
# 视频降码率
ffmpeg -i input.mp4 -vcodec h264 -b:v 1M -acodec aac -b:a 128k output.mp4
代码优化
混淆与压缩
// build-profile.json5
{
"buildOption": {
"enableObfuscation": true, // 混淆
"enableMinification": true, // 压缩
"enableSourceMap": false // Release 关闭 SourceMap
}
}
混淆规则(obfuscation-rules.txt):
# 保留入口和导出
-keep-global-name
entry
# 保留接口和数据类
-keep class com.example.data.**
-keep interface com.example.api.**
# 保留原生互调方法
-keep class com.example.bridge.** { *; }
移除调试日志
// 使用条件编译移除 Debug 日志
const DEBUG = false; // Release 构建设为 false
function log(msg: string) {
if (DEBUG) {
console.log(msg);
}
}
构建时移除(通过宏定义):
// build-profile.json5
{
"buildOption": {
"arkOptions": {
"defines": {
"DEBUG": false
}
}
}
}
Tree Shaking
HarmonyOS SDK 默认启用 Tree Shaking,但需要注意:
// ❌ 全量导入(整个库都会打包)
import * as utils from './utils';
// ✅ 按需导入(未使用的函数会被移除)
import {
formatDate, parseUrl } from './utils';
原生库优化
ABI 分包
// build-profile.json5
{
"buildOption": {
"abiFilters": ["arm64-v8a"] // 仅保留 ARM64(现代设备)
}
}
多 ABI 分发(应用市场支持时):
{
"targets": [
{
"name": "arm64",
"buildOption": {
"abiFilters": ["arm64-v8a"]
}
},
{
"name": "arm32",
"buildOption": {
"abiFilters": ["armeabi-v7a"]
}
}
]
}
移除未使用的 SO
# 检查 SO 依赖
readelf -d libs/arm64-v8a/libexample.so | grep NEEDED
# 移除未引用的库
# 在 CMakeLists.txt 中删除对应 target_link_libraries
分包策略
Feature HAP 按需加载
app.app
├── entry.hap # 主包:核心功能
├── feature1.hap # 特性包:高级功能
└── feature2.hap # 特性包:离线资源
配置 Feature HAP(module.json5):
{
"module": {
"name": "feature1",
"type": "feature", // 类型为 feature
"deliveryWithInstall": false, // 不随主包安装
"installationFree": false
}
}
动态加载 Feature HAP:
import bundleManager from '@ohos.bundle.bundleManager';
async function loadFeature() {
try {
await bundleManager.installModule('feature1');
console.log('Feature 加载成功');
} catch (err) {
console.error('Feature 加载失败', err);
}
}
按需加载大资源
// 将大文件放入 rawfile/online/
// 首次使用时下载
import request from '@ohos.request';
import fs from '@ohos.file.fs';
async function downloadResource(url: string, savePath: string) {
const downloadTask = await request.downloadFile(this.context, {
url: url,
filePath: savePath
});
downloadTask.on('complete', () => {
console.log('资源下载完成');
});
}
构建配置最佳实践
Release 构建完整配置
// build-profile.json5
{
"apiType": "stageMode",
"buildOption": {
"enableObfuscation": true,
"enableMinification": true,
"enableSourceMap": false,
"shrinkResources": true,
"abiFilters": ["arm64-v8a"],
"arkOptions": {
"runtimeOnly": false,
"defines": {
"DEBUG": false
}
}
},
"targets": [
{
"name": "default",
"runtimeOS": "HarmonyOS"
}
]
}
签名与证书
# 生成密钥对
keytool -genkeypair -alias release -keyalg RSA -keysize 2048 \
-validity 10000 -keystore release.p12 -storetype PKCS12
# 配置签名(hvigorfile.ts)
export default {
signingConfigs: {
release: {
storeFile: 'release.p12',
storePassword: '******',
keyAlias: 'release',
keyPassword: '******'
}
}
}
实战案例
优化前后对比
某应用初始包体 45MB,优化后 18MB(减少 60%):
| 优化项 | 减少体积 |
|---|---|
| 图片转 WebP + 压缩 | -12MB |
| 移除未使用资源 | -5MB |
| 代码混淆与压缩 | -3MB |
| 仅保留 ARM64 ABI | -7MB |
持续监控
// 构建后自动检查包体积
// 在 CI 脚本中添加
const fs = require('fs');
const MAX_SIZE_MB = 20;
const stats = fs.statSync('build/entry.hap');
const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
if (sizeMB > MAX_SIZE_MB) {
console.error(`包体积超标: ${
sizeMB}MB > ${
MAX_SIZE_MB}MB`);
process.exit(1);
}
小结
| 优化方向 | 关键手段 | 预期收益 |
|---|---|---|
| 资源 | WebP、移除未使用、压缩音视频 | 30%-50% |
| 代码 | 混淆、Tree Shaking、移除日志 | 10%-20% |
| 原生库 | ABI 分包、移除未使用 SO | 10%-30% |
| 分包 | Feature HAP、动态资源 | 20%-40% |
关键原则:
- 先分析再优化,用数据驱动决策
- 资源优化收益最大,优先投入
- 分包策略适合功能模块清晰的应用
- 在 CI 中监控包体积,防止回退
从 HAP 结构分析到分包治理,HarmonyOS 的包体优化既有通用规律,也有平台特性——掌握这些工具与策略,就能让应用在保持功能完整的同时,持续保持轻量。