Administrator
发布于 2026-09-02 / 5,010 阅读
0
0

鸿蒙包体积优化:把 100MB 砍到 50MB 的工程实践

系列文章《鸿蒙开发从入门到专家》第 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. 用对格式

格式

适用场景

备注

webp

透明背景、图标

同样画质比 PNG 小 25%-35%

heif/heic

摄影类图片

高压缩率,但兼容性需确认

svg

矢量图标、简单 logo

体积小任意缩放

jpg

照片类

不需要透明就别用 png

迁移时建议保留 .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。

策略:

  1. 只保留主流 ABI。鸿蒙手机基本都是 arm64,但智能穿戴可能用 arm32。可以通过 abiFilters 配置:

    // build-profile.json5
    {
      "buildOption": {
        "napiLibFilter": {
          "filterMode": "include",
          "includes": ["arm64-v8a"]
        }
      }
    }
    
  2. 用压缩版 SDK。很多库提供 release-stripped 版本,比 debug 版本小 50% 以上。

  3. 剥离调试符号。检查 .so 里有没有 .debug_* 段,那部分在生产环境没必要打包。

  4. 按需加载 so。System.loadLibrary("xxx") 是按需加载的,不要集中放到 Application 初始化里。

五、分包策略与多包架构

鸿蒙的应用可以拆成多个 HAP:

  • 主包:必装的 Ability。

  • 特性包:动态下载的子功能。

  • 原子化服务包:免安装的轻量服务(见上一篇元服务相关文章)。

体积优化不是只看主包大小,而是算首次安装+典型使用的总体下载量。比如做一个工具类 App,主包 30MB,12 个动态特性包每个 5MB。但用户平均只用到 3 个特性包,那么首次体验仍然是 30MB + 15MB ≈ 45MB,这就是真实度量。

分包架构设计的几个原则:

  1. 首次安装不下载特性包。让用户进应用后再选择下载,避免强制下载用户不需要的功能。

  2. 特性包粒度适中。每个特性包 5-15MB,超过 20MB 用户会有阻力。

  3. 签名统一。多个 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 开发者文档《应用瘦身策略》(思路可借鉴)


评论