系列文章《鸿蒙开发从入门到专家》第 32 篇 · 中级
包体积这件事,老板、用户和商店审核人员都关心,但开发同学往往最后才知道。
用户视角下,应用商店列表里几个产品对比,30MB 的总是比 80MB 的"看着轻",下载等待时间也短一截;商店视角下,包体积超阈值会让搜索权重下降;产品视角下,部分低端机型甚至会因为体积限制而拒绝下载。结果往往是 QA 测出"安装包怎么这么大"甩过来的截图,开发才第一次打开 AAR 或 HAP 工具看一眼。
这一篇把鸿蒙应用包体积优化的常见动作整理出来,按"先看清包结构 → 再分头砍"的顺序讲。
一、先看清包的内部构成
鸿蒙应用的发布单位是 .app 文件,本质是个压缩包。解压后结构大致如下:
myapp.app/
pack.info # 包元信息
signature # 签名
MyModule-signed.hap # 实际安装包
MyModule-default-signed.app
...其他依赖包
.hap(HarmonyOS Ability Package)才是真正的安装包。再次解压:
MyModule-signed.hap/
pack.info
assets/ # 资源(图片、字体等)
entry/
bundleName/
resources/
base/media/ # 默认资源
zh_CN/media/
en_US/media/
ets/ # ArkTS 编译产物
libs/ # 原生库(.so)
resources/ # element/string 等配置
module.json5 # 模块配置
assets 通常是体积大头;ets 次之(视业务复杂度);libs 看原生依赖。三块都能优化,重点不同。
第一步是学会用工具看分布。DevEco Studio 的 Build → Analyzer 可以看到包内各文件大小,类似 Android Studio 的 APK Analyzer。建议把这个数据存到 CI 产物的基线里,每周对比变化。
二、图片资源:最常见的体积大头
鸿蒙应用里 60%-80% 的体积来自图片资源。优化方法按"成本/收益"排序:
1. 用对格式
迁移时建议保留 .png 源文件,构建流程自动转 webp,避免污染设计资产。
2. 按需提供多分辨率
很多团队一刀切放 @3x,但用户的设备分辨率从 1x 到 4x 不等。在 resources/base/media 里只放 1x/2x 主流规格,能减少约 30% 体积。
// resources/base/media/quality.json
{
"分辨率策略": {
"icon类": "1x+2x即可",
"插画类": "2x即可",
"照片类": "1x原图"
}
}
3. 启用压缩和资源混淆
build-profile.json5 中可以配置:
{
"app": {
"buildOption": {
"obfuscation": {
"enable": true,
"obfuscationMode": "obfuscate",
"obfuscationOptions": {
"resourcePrefix": "r_"
}
}
}
}
}
混淆会把 assets/images/login_logo.png 改成 assets/r/_a1.png,实际收益来自缩短路径长度 → 字典压缩更有效。一张图独立文件可能省 2-3 字节,但乘以数千张就是几 MB。
4. 用 media 目录还是 rawfile?
需要走 $r('app.media.xxx') 解析的放 resources/base/media;不需要打包的运行时下载资源放 rawfile。后者体积更小(不走 aapt 路径索引),但访问方式不同——用 getContext().resourceManager.getRawFileContent()。
三、代码瘦身:混淆、Tree Shaking、动态加载
代码层面的优化主要靠三件事:混淆压缩、按需打包、动态加载。
1. 启用 R8/ArkTS 混淆
鸿蒙的混淆工具会自动移除未引用的方法、未使用的字段:
{
"buildOption": {
"obfuscation": {
"enable": true,
"files": ["./obfuscation-rules.txt"]
}
}
}
obfuscation-rules.txt 文件示例:
-disable-obfuscation
-keep class com.huawei.myapp.entry.EntryAbility { *; }
-keep,allowobfuscation class com.huawei.myapp.data.User { *; }
EntryAbility 这类入口类不能混淆(系统反射调用),但内部类如 User 可以混淆名字。
混淆后体积普遍能下降 5%-10%,但更重要的是逆向门槛提高一项。
2. Tree Shaking 和按需 import
ArkTS 在打包阶段会做类似 Tree Shaking 的优化,但只在 import 是静态且无副作用时有效。几种常见反模式:
// 反例:整包导入
import * as utils from './utils';
utils.parseA(...);
// 正例:仅导入需要的方法
import { parseA } from './utils';
parseA(...);
// 反例:常量整包全打进去
import lodash from 'lodash';
// 正例:拆解到 babel-plugin-lodash 或手写工具函数
import map from 'lodash/map';
很多团队觉得 ArkTS 编译优化很强,实际上没有静态分析可达的工具函数、动态 import、runtime require 都会绕过 Tree Shaking,需要代码侧主动配合。
3. 动态加载:用得少就拆出去
@ohos.router 已经支持运行时加载 HAP/HSP 模块,把不常用的业务(如"年度报告""年度报告导出 PDF"这种一年只用一次的功能)拆成独立 HAP,需要时再下载:
// 主包
import('feature-share').then(({ ShareModule }) => {
ShareModule.openSharePage();
});
构建项目时把它标为独立 HAP,主包体积直接少一半。
四、原生库(.so)的精简
如果你的应用用了第三方 SDK,比如 FFmpeg、TFLite、SQLCipher,每个库的 arm64-v8a、armeabi-v7a、x86_64 三种 ABI 都要打,体积轻松上 30MB。
策略:
只保留主流 ABI。鸿蒙手机基本都是 arm64,但智能穿戴可能用 arm32。可以通过
abiFilters配置:// build-profile.json5 { "buildOption": { "napiLibFilter": { "filterMode": "include", "includes": ["arm64-v8a"] } } }用压缩版 SDK。很多库提供 release-stripped 版本,比 debug 版本小 50% 以上。
剥离调试符号。检查
.so里有没有.debug_*段,那部分在生产环境没必要打包。按需加载 so。
System.loadLibrary("xxx")是按需加载的,不要集中放到 Application 初始化里。
五、分包策略与多包架构
鸿蒙的应用可以拆成多个 HAP:
主包:必装的 Ability。
特性包:动态下载的子功能。
原子化服务包:免安装的轻量服务(见上一篇元服务相关文章)。
体积优化不是只看主包大小,而是算首次安装+典型使用的总体下载量。比如做一个工具类 App,主包 30MB,12 个动态特性包每个 5MB。但用户平均只用到 3 个特性包,那么首次体验仍然是 30MB + 15MB ≈ 45MB,这就是真实度量。
分包架构设计的几个原则:
首次安装不下载特性包。让用户进应用后再选择下载,避免强制下载用户不需要的功能。
特性包粒度适中。每个特性包 5-15MB,超过 20MB 用户会有阻力。
签名统一。多个 HAP 共享一个签名,否则安装时会冲突。
六、自动化检测与红线
包体积优化最怕"今天砍了 5MB,下个迭代又涨回来"。两个动作是必须的:
1. 在 CI 里卡红线
# 伪代码:gitlab-ci.yml 中
package_size_check:
stage: verify
script:
- ./scripts/inspect_hap.sh app/build/outputs/app/default/entry-default-signed.hap
- python3 check_size.py --max 60 # 单位 MB
allow_failure: false
这样一旦超过阈值合并会被拒绝,避免体积偷偷增长。
2. 可视化趋势
把每周包体积上传到一个 dashboard(自建或 AppGallery Connect 都行),形成曲线。很多公司的体积膨胀不是某个单一改动,而是每周"加个小图标"——有个图盯着才会警觉。
七、几个不优化包也踩过的坑
图片忘了删除。原型设计师给你的图,最后上线时被忘了,默默躺在包里。建议定期执行
find . -size +500k。大文件原图误打包。设计同学原图替换了设计稿的 jpg 没压缩,直接进了仓库。
第三方 SDK 全家桶。一些 SDK 30+MB 默认集成了所有能力,按需开启功能。
不要等到上架前才回头看包结构。每周打开 Analyzer 看一眼,是性价比最高的"主动预防"。
小结
包体积优化的核心思路:先测量,后下手;分门别类,按影响排序。
图片:格式选对、分级提供、混淆路径。
代码:混淆 + Tree Shaking + 动态 import。
原生库:ABI 过滤 + strip + 按需加载。
多包架构:主包小、特性包按需下载。
下一篇我们要解决另一个安全侧的事——怎么保护你的代码和资源不被逆向,主要谈混淆、反编译、密钥和反调试。
参考阅读
鸿蒙开发者文档《应用包结构说明》
鸿蒙开发者文档《代码混淆指南》
华为 AppGallery Connect《应用瘦身最佳实践》
Android 开发者文档《应用瘦身策略》(思路可借鉴)