升级适配时最容易漏掉的不是一个新接口,而是一行旧判断:if (version >= '26.0.0')。这行代码比较的是字符串,不是三个数值。假设以后出现 26.10.026.9.0,字典序会把前者排在后者前面还是后面?更棘手的是,旧版本还带括号,例如 6.1.1(24);直接 split('.') 会把 1(24) 当成一个普通修订号。

HarmonyOS 7 的 API 26 把版本号格式改成 X.Y.Z。这篇只处理版本字符串解析与比较,不把它冒充成系统能力检测。后面两个例子说明:哪种比较会错、怎样把旧格式和新格式明确分流。

把主、次、修订版本拆成数值后比较

官方变化到底是什么

华为的版本号格式调整说明于 2026 年 8 月 29 日更新:从 API 26.0.0 起采用 X.Y.Z 语义化格式。X 是主版本,Y 是次版本,Z 是修订版本。此前的格式为 X.Y.Z(N),括号里的 N 表示 OpenHarmony 底座 API level。官方也提醒涉及 API 兼容判断的代码要随格式变化调整。

26.0.126.1.027.0.0 在官方说明里是格式示例,不代表已经发布或未来版本规划。下文的 26.9.026.10.0 也只是用来暴露字符串排序错误的测试值,不能当成真实设备版本。

输入应识别成不该做的事
26.0.0新式三个数值段当成小数 26.0
26.10.0新式 [26,10,0] 测试值按字符串与 26.9.0
6.1.1(24)旧式 [6,1,1] 加底座 API level 241(24) 当成数字,或扔掉括号

案例一:为什么 26.10.0 可能比 26.9.0“小”

用字符串比较,比较到 26. 后,下一个字符分别是 19,于是 26.10.0 被排在 26.9.0 前面。这与数值段比较的结果相反。即使今天的设备没有这两个版本,这种写法仍是一个确定的逻辑缺陷。

正确做法是在确认是新式 X.Y.Z 格式之后,先把每段解析为整数,再按主、次、修订顺序逐段比较。不要把整个版本串转为浮点数;26.1026.1 用小数表达会丢失语义。

export function parseApiVersion(input) {
  const semantic = /^(\d+)\.(\d+)\.(\d+)$/.exec(input);
  if (semantic) {
    return { format: 'semantic', parts: semantic.slice(1).map(Number) };
  }
  const legacy = /^(\d+)\.(\d+)\.(\d+)\((\d+)\)$/.exec(input);
  if (legacy) {
    return {
      format: 'legacy',
      parts: legacy.slice(1, 4).map(Number),
      baseApiLevel: Number(legacy[4]),
    };
  }
  throw new Error(`Unsupported API version format: ${input}`);
}

export function compareSemanticApiVersions(left, right) {
  const a = parseApiVersion(left);
  const b = parseApiVersion(right);
  if (a.format !== 'semantic' || b.format !== 'semantic') {
    throw new Error('Do not compare legacy and semantic version strings as one scale');
  }
  for (let i = 0; i < 3; i++) {
    if (a.parts[i] !== b.parts[i]) return Math.sign(a.parts[i] - b.parts[i]);
  }
  return 0;
}

本地测试里,compareSemanticApiVersions('26.10.0','26.9.0') 得到 1,而两个 26.0.0 得到 0。返回值只表达这个受限的新格式比较器的顺序,不表示设备已具备某个具体 API。

案例二:6.1.1(24) 不能“去掉括号后”并入新规则

旧格式的括号值是一个单独的底座 API level,不是 Z 的小数部分。把 6.1.1(24) 清洗成 6.1.1 会丢失信息;把 24 当成 X 来和 26 比,又混用了两套定义。最稳妥的做法是先识别格式,旧式留在旧式分支,新式才进入三段语义版本比较。

上面的解析器会把它拆成:

format = legacy
parts = [6, 1, 1]
baseApiLevel = 24

compareSemanticApiVersions('6.1.1(24)', '26.0.0'),函数主动抛错,而不是给一个看似确定、实际语义不清的大小关系。业务真正要判断的是“某个 API 能不能用”时,应以华为该接口的起始版本、设备能力与应用的兼容配置为依据;版本展示字符串只是输入之一。升级到 API 26 前,还需要按官方升级适配指导核对弃用接口和行为变化,并在新旧系统设备上验证。

如何复现和验收

我把与正文一致的解析器用 Node.js 跑了三组测试:数值段排序、旧格式分流与跨格式拒绝、非法格式拒绝。结果 3 pass / 0 fail。其中 26.9.026.10.0 是构造数据;测试通过只证明解析器的输入输出,不证明任何未发布 API 版本的真实存在。

实际工程里可再补一张输入矩阵:来自构建配置的版本、设备设置显示的版本、服务端传下来的版本,逐一标明来源和格式。对未知或带预发布后缀的字符串,不要悄悄截断;要么使用已验证的标准版本库处理完整 SemVer,要么明确拒绝并记录来源。上面的小解析器只支持官方说明里的纯数字新旧格式,不声称实现了完整 SemVer 规范。

我更愿意让它作为一个输入校验器,而不是到处散落的 version >= '...'。格式变化时先让不认识的值显式失败,再用接口文档和设备测试决定能力分支;这样一次迁移不会在十几个页面里留下十几种不同的误判。

参考:版本号格式调整说明 · 应用兼容性说明 · 升级到 26.0.0 指导

Logo

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

更多推荐