很多人把体积预算看成代码组织问题,实际它首先是正确性问题。校园运动会应用上线前发现安装包体积突然增长一旦出现“新增大图片和重复音效后产物明显变大,但团队只看源码行数,没人知道增长来自哪个目录”的路径,用户看到的状态就可能与系统最终处理的内容不同。

技术主题封面

观察到的现象

包体积增长不一定来自新增代码。一个图片被反复拷贝到不同资源目录、一个调试素材误入正式包,可能比几百行 ArkTS 更影响最终交付。团队应从构建产物而非源码感受来判断变更。

“构建包体积由代码、资源、依赖与打包选项共同决定,应比较真实产物而非开发目录大小”是这篇最重要的设计判断。单看成功路径,校园运动会应用上线前发现安装包体积突然增长可以靠若干事件回调工作;但只要用户中途修改、取消或重复操作,若没有清晰约束,就无法确定最终输出究竟属于哪一次输入。工程拆分必须以这些不确定性为起点。

我们希望得到的不是一张孤立的效果图,而是一条可追溯的处理链。整个过程拆为构建产物、资源列表、基线对比、阈值判断、修复复核。每一段只承诺自己负责的结果,下一段不靠猜测前一步的内部状态。对体积预算而言,这种清晰分工也是回归测试能够写得具体的前提。

预算机制必须有基线。首先保留上一个可发布构建的资源清单,再让本次构建输出可比较的条目;按路径与大小计算差异,才能知道是同一资源变大,还是新增资源导致总体增加。

隐藏在现象后的机制

实施选择是:将每次构建的资源体积列表与基线对比,设置 warning 和 fail 阈值,并把异常归属到具体资源。这里不能倒过来,先把组件和请求写完,再向现有代码里硬塞判断。规则入口只接收必要数据,输出也尽量可序列化。这样在没有设备的环境下仍能对核心逻辑做确定性检查。

把“构建产物”和“资源列表”分成两个阶段,是为了让每个阶段都有可检查的输入与输出。校园运动会应用上线前发现安装包体积突然增长并不需要复杂框架才能做到这一点,真正需要的是一个稳定的数据契约:前一步产出的值能被下一步清楚识别,失败时也能停在正确的位置。

从“资源列表”走到“基线对比”时,不要直接复用页面当前显示的全部状态。界面可能正在更新,用户也可能继续操作。此处应只传递本阶段约定的数据,并记录这份数据属于哪次动作,减少跨阶段读到不同版本的风险。

“基线对比”阶段很容易被认为已经完成了整个功能,其实它只提供一个前提。只有进入“阈值判断”之后,才能知道前一步是否真的满足业务要求。把这个检查写成显式返回值,比依赖某个内部字段在恰当时刻改变更稳健。

示例中的二十兆预算只是用于解释管理规则,不属于任何项目的真实测量值。真正上线时需要根据设备、下载策略和产品约束设定阈值,并在 CI 中对实际 HAP/APP 产物进行核验。

体积预算的关键设计

把约束写进实现

下面给出可以独立检查的关键实现。代码只覆盖本篇要证明的核心行为;涉及工程权限、服务端协议或构建设置的条件已在上下文列明。它应被放入明确的模块中,而不是把所有业务逻辑堆进 ArkUI build()。

interface SizeItem { name: string; bytes: number }
interface PackageReport { baseline: number; files: SizeItem[] }
function audit(r: PackageReport, budget: number): string[] {
  const sum = r.files.reduce((n: number, f: SizeItem) => n + f.bytes, 0)
  const warnings: string[] = []
  if (sum > budget) warnings.push('资源体积超过预算')
  if (sum > r.baseline) warnings.push('比基线增加:' + (sum - r.baseline))
  r.files.filter((f: SizeItem) => f.bytes > 2 * 1024 * 1024)
    .forEach((f: SizeItem) => warnings.push('大资源:' + f.name))
  return warnings
}
// 示意核对资源列表,不能代替真正读取 HAP 或 APP 构建产物。

应区分 warning 与 fail。非核心大资源可以给出警告并要求责任人解释;超过正式发布门槛则阻止进入签名与上架环节。只比较总字节可能漏掉单个巨大资源,因此还要检查最大资源项。

真实流水线需要从构建产物提取资源清单再传给 audit;下面是构建命令,不可把示意数据当作已测得的包体积。

# 在工程根目录执行,构建参数以当前 DevEco 工程为准
hvigorw --mode module -p product=default -p buildMode=release assembleHap
# 产出 HAP 后使用实际包分析工具提取资源列表,再计算差异。

代码中的关键判断来自业务约束,而不是某个屏幕比例。以“基线对比”为界,输入应先满足所属阶段的前置条件;当判断失败时,返回错误或保留状态比直接抛出无法解释的页面状态更可控。对需要 UI 反馈的部分,随后由组件转换为明确的文案。

如果把上述核心逻辑接到一个 ArkUI 页面,推荐组件仅处理状态投影和交互事件:例如 TextInput.onChange 接收新的字符串,Button.onClick 触发一次显式动作,得到结果后再设置 @State 驱动展示。对于不是输入框的体积预算功能,则采用相同的单向数据边界而不强行引入 TextInput。

体积预算的验证示例

输入越界以后会发生什么

最应该提前考虑的风险是:示例预算数值只是管理规则,不能伪称当前项目已有测量或签名包实际缩减。这句话不是发布时随手附加的注意事项,而是编码时必须坚持的限制。只要异常路径缺乏明确处理,下一次回归就可能把一个本来可恢复的状态变成难以判断的失败。

验证可以在测试分支里加入一张体积明显较大的 PNG,观察报告是否精确定位它,然后删除并重新核查。这个过程是在验证体积门禁逻辑,不是宣称图片压缩已经在设备上取得某种性能提升。

重复验收。 连续两次执行资源列表,再执行基线对比,检查第二次动作是新意图还是前次动作的继续。对于体积预算尤其要明确重复处理规则;“按钮点了两次”是用户行为事实,并不自动意味着应当产生两份业务副作用。

错误恢复。 在基线对比成功、阈值判断尚未完成的位置构造异常。应能说明数据暂存在哪里,用户看到何种反馈,是否允许重新触发,以及什么条件下必须停止。不要为了追求一张好看的页面而隐藏正在等待确认的真实状态。

跨版本核对。 在数据和配置持续演进的前提下,固定一份输入,记录预期输出;随后修改不相关的界面颜色或布局,再运行同一用例。如果结果变化,说明体积预算与展示层之间出现了不应存在的隐性耦合。

边界值核对。 不只输入一个“正常例子”,还需要空字符串、用户明确的否定值、最大长度附近以及重复内容等不同形态。对体积预算来说,边界值是否被恰当表达,往往比用户第一次成功完成流程更能反映实现质量。

对下一次改造的启示

性能、可访问性与资源预算是不同质量维度。压缩包变小并不保证运行更快,误删资源甚至可能导致界面空白;压缩策略必须同时核对显示清晰度和资源引用,不能只追求数字下降。

本篇可用以下结果作为验收目标:增加一张过大 PNG 后差异清单准确指向它,删除无用资源后预算重新通过。检验时先从固定输入入手,再加入中途切换、重复操作和失败恢复。可解释的错误结果也属于正确实现的一部分;没有必要为了让每个步骤都显示绿色而悄悄跳过不满足的前置条件。

关于示例预算数值只是管理规则,不能伪称当前项目已有测量或签名包实际缩减这一风险,建议把它写进实际测试用例的名称和断言,而不是只放在代码注释。注释会随着重构失效,测试中的输入与预期结果更容易暴露行为变化。测试本身同样要避免依赖随时变化的网络与时间,否则会把产品故障和测试环境偶然因素混在一起。

当方案需要扩展,优先问“新需求将改变哪个阶段”。如果它只改变资源列表,便不应触及后面的基线对比;如果它改变最终业务语义,规则层和序列化层才需要一起调整。这个决策方式能够避免每加一个按钮就复制一份流程逻辑,也使连载中的代码可以逐篇成长。

对于业务异常,提示文案至少要回答当前状态、可以继续的动作以及不能继续的原因。校园运动会应用上线前发现安装包体积突然增长里的用户通常不关心内部类名或错误码,但需要知道是等待、重试还是修改输入。把工程错误类别映射为用户可理解的行为建议,比把原始异常堆栈放在对话框里更合适。

交付的验证证据应当与主张相称。纯函数逻辑可以使用固定输入输出来证明;界面效果只能由真实运行或明确标注的示意展示;网络限流、签名、权限等涉及外部系统的结论,需要在对应环境中完成复核。不要把静态检查结果提升为已经完成真实设备测试的结论。

这类问题的适用边界同样清楚:构建包体积由代码、资源、依赖与打包选项共同决定,应比较真实产物而非开发目录大小。当具体 SDK、设备能力或服务端协议发生变化,应重新查看对应的官方接口约束,再重新跑上述场景验证,而不是根据一张示例图推断系统行为。

官方资料

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐