ArkGuard API26 压缩排除规则封面

HarmonyOS 7 ArkGuard 配了 -keep-uncompact 却没生效?先查 -compact 和相对路径

一个模块升级后,Debug 包能跑,Release 包却在某个反射或动态调用入口出错。看到 API 26 新增的 -keep-uncompact,很容易把它当成“不要动这个文件”的总开关,往规则文件里写一行就收工。我更愿意先停一下:这个选项只排除代码压缩,不等于保留所有类名、属性名或文件名。

华为 2026-09-10 更新的 ArkGuard 保留选项文档写了三个边界:-keep-uncompact 从 API 26.0.0 开始;只有启用了 -compact 才生效;路径是相对于混淆规则文件所在目录的相对路径。远程三方包要按项目级 oh_modules 中的真实路径配置。本文只谈这个新选项如何生效,不把它说成所有混淆问题的万能修复。

验证范围:下方 Node.js 小脚本在本机跑通了规则前置检查。它只验证配置文本与路径计算,没有执行 HarmonyOS 7 的 ArkGuard 构建。最终是否保留目标结构,要以装有 API 26 SDK 的工程 Release 构建产物和功能回归为准。

先把三个选项分清

规则解决的问题不负责什么
-compact开启代码压缩不替你处理动态访问的名字兼容
-keep-uncompact将指定相对路径排除在代码压缩之外不等于 -keep,也不自动保留所有属性名
-keep / -keep-property-name按规则保留名称不应为了一个小范围问题就无差别保留整个工程

先确定故障原因是压缩改变了目标代码的组织,还是混淆改了外部协议依赖的名称。例如 JSON 序列化字段名变化,应该检查对应属性保留规则;单独给这个目录加 -keep-uncompact 并不能解释字段名为什么变化。反过来,若名字已经正确保留,但压缩后的产物行为异常,才进一步对比排除压缩前后。

ArkGuard 压缩排除规则生效条件图

案例一:只写排除规则,忘了开启压缩

假设规则文件位于 entry/obfuscation-rules.txt,目标文件位于 entry/src/main/ets/bridge/LegacyAdapter.ets。这段配置不会让排除规则生效

# entry/obfuscation-rules.txt
-keep-uncompact
./src/main/ets/bridge/LegacyAdapter.ets

因为本来就没有 -compact。如果 Release 包没有压缩,排除压缩当然观察不到差异。对照组应写成:

# entry/obfuscation-rules.txt
-compact
-keep-uncompact
./src/main/ets/bridge/LegacyAdapter.ets

这并不是建议所有项目马上打开 -compact。若工程原本没启用压缩,先确认是否真的要引入它,再做完整 Release 回归;不能为了验证一条新规则,顺手改变整个应用的构建行为。

我会准备两个 Release 产物做比较:保持其他构建选项相同,只改变排除规则;记录包体、目标模块产物和相同测试用例。若两组都没有启用压缩,比较结果不能证明 -keep-uncompact 有用。

案例二:entry 写了两次,保护到不存在的位置

规则文件在 entry/ 下时,./这个文件所在目录出发,不是从仓库根目录出发。把工程树想成:

project/
  entry/
    obfuscation-rules.txt
    src/main/ets/bridge/LegacyAdapter.ets

应写 ./src/main/ets/bridge/LegacyAdapter.ets。如果写成 ./entry/src/main/ets/bridge/LegacyAdapter.ets,实际相对到 project/entry/entry/...,根本没有命中那个文件。路径错误不会因为规则名字看起来正确而自动修好。

三方包还有一层容易踩的坑:官方要求对远程三方包指向项目级 oh_modules 中的真实路径,不能只凭编辑器里看见的模块级软链接位置猜路径。包升级后真实目录若变化,规则也要复查。

本地可运行的规则预检

这段脚本不冒充 ArkGuard。它只把上述两个前置条件做成自动检查:是否有 -compact,以及相对路径是否解析到已知目标。为方便复现,脚本用内存中的工程文件集合,不改你的项目。

import assert from 'node:assert/strict';
import path from 'node:path';

const configDir = 'C:/demo/entry';
const files = new Set([
  'C:/demo/entry/src/main/ets/bridge/LegacyAdapter.ets',
  'C:/demo/oh_modules/vendor/src/index.ets'
]);

function inspect(rules, target) {
  const lines = rules.split(/\r?\n/).map(s => s.trim())
    .filter(s => s && !s.startsWith('#'));
  const compact = lines.includes('-compact');
  const at = lines.indexOf('-keep-uncompact');
  const relative = at < 0 ? '' : lines[at + 1];
  const resolved = relative && relative.startsWith('.')
    ? path.posix.normalize(path.posix.join(configDir, relative)) : '';
  return {
    compact,
    resolved,
    targetExists: files.has(target),
    pathMatches: resolved === target,
    ready: compact && files.has(target) && resolved === target
  };
}

const target = 'C:/demo/entry/src/main/ets/bridge/LegacyAdapter.ets';
const noCompact = inspect('-keep-uncompact\n./src/main/ets/bridge/LegacyAdapter.ets', target);
assert.equal(noCompact.ready, false);
assert.equal(noCompact.pathMatches, true);

const wrongPath = inspect('-compact\n-keep-uncompact\n./entry/src/main/ets/bridge/LegacyAdapter.ets', target);
assert.equal(wrongPath.ready, false);
assert.equal(wrongPath.pathMatches, false);

const correct = inspect('-compact\n-keep-uncompact\n./src/main/ets/bridge/LegacyAdapter.ets', target);
assert.equal(correct.ready, true);

const packageTarget = 'C:/demo/oh_modules/vendor/src/index.ets';
const packageRule = inspect('-compact\n-keep-uncompact\n../oh_modules/vendor/src/index.ets', packageTarget);
assert.equal(packageRule.ready, true);
console.log('4 checks passed: switch, wrong path, correct path, project-level package path');

保存为 arkguard-rule-check.mjs 后运行 node arkguard-rule-check.mjs。本机输出应为 4 checks passed...。这是配置预检,不是产物检验;目标在真实工程里可能因目录结构、构建配置或 SDK 差异而不同,不能拿这行输出代替 Release 回归。

真正接入工程怎么验

  1. 在目标 API 26 SDK 下确认项目是否启用了源码混淆、-compact 是否开启,规则文件实际被哪个模块的构建配置引用。没被加载的规则文件改得再对也没用。
  2. 从规则文件所在目录计算目标文件的相对路径;对项目级三方包使用真实路径。先缩到一个文件或目录,不要一上来排除全部模块。
  3. 固定相同源码、构建模式和依赖版本,分别构建有排除规则和无排除规则的 Release 包。比较目标产物,同时跑反射、序列化、动态调用等真正出问题的入口
  4. 若产物仍异常,分开排查名称混淆与代码压缩:名称问题查 -keep-keep-property-name 等对应规则;不要继续堆 -keep-uncompact
  5. 记录规则引起的包体与性能变化。保护范围越大,潜在优化收益损失越大;只保留能用回归用例证明必要的最小范围。

这些检查比“配置了规则,Debug 能跑”严格得多。Debug 正常只能说明另一种构建条件下正常,不能证明 Release 的压缩排除已经命中。

什么时候不该用它

如果根因是 JSON 字段名被改、NAPI 导出名不匹配或路径字符串依赖文件名,那么 -keep-uncompact 就不是第一选择;先用对应的名称保留规则和官方混淆助手定位。如果根本没有开启 -compact,这个选项没有效果。如果只在某一机型异常,先拿相同构建产物复现,别把设备差异误判成压缩问题。

我的结论很简单:API 26 给了我们更细的“排除代码压缩”工具,但它的价值在于小范围、有对照、有验收。先证实开关、路径、规则文件归属,再看真实产物和故障入口。不要拿它当“关掉一切混淆”的总开关。

参考资料(核对日期 2026-09-18):

Logo

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

更多推荐