HarmonyKit | 鸿蒙开发:性能调优与 HAP 体积控制实践
HarmonyKit | 鸿蒙开发:性能调优与 HAP 体积控制实践
引言:工具应用不需要"极致性能"
HarmonyKit 作为工具类应用,性能优化不是"从 100ms 优化到 50ms"的微观调优,而是宏观的"体积-启动-流畅"三角平衡。HAP 体积约 3MB、冷启动 < 500ms、工具页切换无感知延迟——这些数字在大多数设备上已经足够好。
但"足够好"不等于"不需要关注"。这篇文章记录了 HarmonyKit 在体积、构建速度、运行时和电池四个维度的优化实践,以及优化过程中用到的一套"先测量、再优化、后验证"的方法论。
项目仓库:https://atomgit.com/VON-/harmony-kit
优化前的测量
所有优化都建立在测量基础之上。HarmonyKit 的优化决策基于以下数据:
| 指标 | 优化前 | 目标 | 备注 |
|---|---|---|---|
| HAP 体积 | ~3.2MB | < 3MB | 主要来自系统资源和 Kit 框架 |
| 冷启动时间 | ~600ms | < 500ms | 模拟器环境,真机会更快 |
| 增量编译 | ~1.5s | ~1s | 改一个文件到编译完成 |
| 全量编译 | ~8s | ~7s | clean build |
| 工具页切换 | < 200ms | 无感知 | 已在目标范围内 |
| 工具计算 | < 10ms | 同步完成 | 所有工具轻量计算 |
从数据可以看到:最大的优化空间在启动时间,而工具页切换和计算已经足够快。所以优化资源被集中投入到启动加速上。
体积控制
HAP 约 3MB 的构成:纯代码 200KB、系统资源(图标/启动图)2MB、系统 Kit 框架自带。零第三方 npm 库是关键——没有依赖意味着零 node_modules 膨胀。
具体的体积优化措施包括:
1. 图片资源压缩
原始的启动图(startIcon)使用了未压缩的 PNG,单张约 500KB。通过以下方式减少:
- 使用 TinyPNG 压缩 PNG 图标,保留透明通道的前提下减少 60-70% 体积
- 使用矢量图(SVG/XML)替换简单的位图图标
- 启动图使用 JPEG 代替 PNG(不需要透明通道),压缩率更高
2. 避免误用资源文件
HarmonyOS 的 resources 目录支持按设备类型分发资源。HarmonyKit 目前只支持手机(deviceTypes: ["phone"]),不需要为 tablet、wearable 等提供资源文件。build-profile.json5 中明确只声明 phone,编译器会自动裁剪掉其他设备类型的资源。
3. 代码瘦身
AOT 编译已经做了代码裁剪(未使用的函数不会被编入 HAP),所以"删掉未使用的代码"在 HarmonyKit 中已经由编译器自动完成。但对于那些"写死了但未使用"的资源引用(如冗余的 string 定义),手动清理仍然有价值。
4. HAP 体积拆解
约 3MB HAP 构成: ├── 编译代码(.abc 字节码) ~200KB (6.7%) ├── 图片/图标/启动图 ~2MB (66.7%) ├── 配置文件(json5) ~10KB (0.3%) └── 系统 Kit 框架内置 ~800KB (26.7%)超过 66% 的体积是图片资源。如果有进一步的瘦身需求,优化方向应该是:减少图片数量和分辨率、使用 8 位 PNG 代替 32 位、考虑 WebP 格式。
冷启动优化
冷启动优化的核心目标是:从用户点击应用到见到主页界面的时间 < 500ms。
1. 启动图策略
HarmonyKit 的startWindowBackground和startWindowIcon设置为纯色 + 简洁图标,而不是复杂的启动页面。原因是:
- 纯色背景不需要绘制,系统可以直接使用
- 简洁图标加载快,不会阻塞主线程
- 复杂启动图(如全屏插图)会增加绘制时间,延长感知启动时间
2. 资源懒加载
HarmonyKit 不需要懒加载图片或数据——因为所有工具都是本地计算,没有网络请求。但"懒加载"的理念体现在 Tab 的初始化上:
// HdsTabs 的默认行为:只初始化当前显示的 Tab// 用户滑动到其他 Tab 时,对应的 TabContent 才创建HdsTabs(){TabContent(){this.ToolGrid('format')}// 初始加载TabContent(){this.ToolGrid('encoder')}// 滑动到才加载TabContent(){this.ToolGrid('converter')}// ...TabContent(){this.ToolGrid('generator')}TabContent(){this.ToolGrid('analyzer')}}如果 5 个 Tab 在启动时同时初始化,5 个 Grid + 所有 ToolCard 需要同时渲染,启动时间会显著增加。HdsTabs 的懒加载机制避免了这个问题。
3. 同步初始化
aboutToAppear(){// 同步初始化,不等待异步操作// 所有工具的初始状态在 build() 之前就准备好了}所有的 aboutToAppear 方法都是同步的——不发起网络请求、不等待定时器、不做耗时的文件 I/O。这意味着组件在创建后可以立即渲染,不需要"加载中"状态。
运行时性能
所有工具的计算在 UI 线程同步执行。无网络、无数据库、无文件 I/O。唯一异步操作是哈希计算,使用 async/await 不阻塞 UI。
1. UI 线程计算
HarmonyKit 10 个工具的核心计算都是 CPU 轻量级的:字符串操作、数字运算、正则匹配、JSON 解析。每个计算都在微秒级别完成,不会导致 UI 卡顿。
以最"重"的哈希计算为例——即使是最耗时的 SHA-256,输入 1KB 文本的计算时间也在 1ms 以内。10 个工具没有一个需要 Web Worker 或 Task Pool。
2. ForEach 的键生成
ForEach 的第二个参数keyGenerator用于优化列表刷新:
ForEach(this.results,(item:RadixResult,index:number)=>{this.ResultRow(item.label,item.value,index)},(item:RadixResult,index:number)=>index.toString()// 用 index 作为 key——项目顺序不变时不会触发重渲染)在 HarmonyKit 中,工具页的数据变动通常是"全部重算"(用户输入变化后重新计算所有结果),而不是"局部增删"。所以用 index 作为 key 是安全的——每次重算产生的新数组和旧数组的对应关系是稳定的。
如果未来加入"可添加/删除项目的列表"(如进制转换中自定义进制数),就需要用稳定的 ID(如 UUID)作为 key。
3. 状态更新的批量处理
避免在同一个事件处理函数中连续多次修改 @State:
// 不好:三次赋值触发三次渲染updateAll(value:number){this.resultA=value*1;// 触发渲染this.resultB=value*2;// 触发渲染this.resultC=value*3;// 触发渲染}// 更好:ArkUI 会自动批量合并同一 tick 的状态变更updateAll(value:number){this.resultA=value*1;this.resultB=value*2;this.resultC=value*3;}ArkUI 的运行时会将同一事件循环内的多次@State修改合并为一次渲染。所以不需要手动做批量优化——但前提是这些修改发生在同一个同步调用链中。如果分散在多个 setTimeout/Promise.then 中,每个都会触发一次独立渲染。
构建速度
增量编译(改一个文件)约 1-1.5 秒。全量编译约 7-8 秒。hvigor --daemon守护进程让后续编译利用缓存。
优化构建速度的核心是:让增量编译覆盖尽可能多的场景。在 HarmonyKit 的开发中,90% 的代码修改都在增量编译的覆盖范围内。只有以下场景触发了全量编译:
- 切换分支
- 修改 build-profile.json5
- 升级 SDK 版本
- clean build
电池优化
HdsTabs 的 ADAPTIVE 材质在低电量时自动降级为 SMOOTH——模糊关闭、光晕禁用。系统自动管理,用户无感知。
HarmonyKit 没有后台任务、没有定位服务、没有网络轮询——这意味着它在后台运行的功耗几乎为零。HdsTabs 的 ADAPTIVE 材质也仅在 API 23+ 的设备上生效,旧设备使用 SMOOTH 材质,功耗自然更低。
性能优化的"三不"原则
- 不提前优化:在测量数据明确显示瓶颈之前,不做性能优化
- 不过度优化:启动时间从 500ms 优化到 450ms 的收益远低于"新增一个工具"的收益
- 不牺牲可维护性:如果性能优化导致代码难以理解,找更好的方案
这个原则在 HarmonyKit 中体现为:性能优化只在"有明显感知问题"时进行。目前没有用户反馈过"这个工具打开太慢"或"运行卡顿",所以性能优化到此为止。
项目仓库:https://atomgit.com/VON-/harmony-kit
