From 600378f7239a27475153e6abc7da2ab870b2e901 Mon Sep 17 00:00:00 2001 From: lanterngamescn Date: Sat, 27 Jun 2026 09:18:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AD=90=E6=B8=B8=E6=88=8F/?= =?UTF-8?q?=E5=A4=A7=E5=8E=85=E6=AF=8F=E6=AC=A1=E5=90=AF=E5=8A=A8=E9=83=BD?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E4=B8=8B=E8=BD=BD=EF=BC=9Aversion.xml=20?= =?UTF-8?q?=E6=94=B9=E7=94=A8=E6=AD=A3=E5=88=99=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现象:子游戏 zip 已下载解压、version.xml 落盘正常,但再次进入仍重新下载; 远程版本与本地一致本应跳过下载。真机诊断([VERXML-DIAG]/[SUBGAME-DIAG]): installed=true、version.xml 原文完整()、gameId 正确解析为 a52y...,唯独 localVer 恒为 0 → 155<=0 判否 → 每次都判需更新而重下。大厅同理 (localVersion 恒 0,启动期每次重下整包)。 根因:VersionXml.parse 用 @kit.ArkTS xml.XmlPullParser,其 attributeValueCallbackFunction 对本文件 的 value 属性不稳定触发(同文件 的 id 却能取到),逻辑自相矛盾——value 无条件捕获却落空,说明该属性回调 未按预期触发。 修法:弃用 XmlPullParser,改确定性正则直接从原文抽 version/gameId/gameName,语义 等价 Android pullxml1,规避解析器回调时序/属性触发的不确定性。根 无属性 (紧跟 '>'),\s+ 自然跳过,只命中带属性的子 game。 验证:真机覆盖安装后再次进入三个老K,localVer=155、不再下载,直接进 H5。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/main/ets/resource/VersionXml.ets | 70 ++++++++----------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/domain_resource/src/main/ets/resource/VersionXml.ets b/domain_resource/src/main/ets/resource/VersionXml.ets index 5d9c916..32e2839 100644 --- a/domain_resource/src/main/ets/resource/VersionXml.ets +++ b/domain_resource/src/main/ets/resource/VersionXml.ets @@ -1,15 +1,15 @@ /** - * version.xml 解析(契约 §5.3,框架 §8.3)。用 @kit.ArkTS xml.XmlPullParser。 + * version.xml 解析(契约 §5.3,框架 §8.3)。 * * 结构: * 用途:取 与远程 game_version 比较决定是否更新;取子 - * 作 app_data 的 app_gamename。`value` 为 version 标签独有;`name` 多标签共有,故按 - * 所属标签(当前为子 game)区分抓取。 + * 作 app_data 的 app_gamename;取子 作大厅 gameid 为空时的回退匹配 key。 + * + * 实现说明:早期用 @kit.ArkTS xml.XmlPullParser,实测其 attributeValueCallbackFunction 对 + * 本文件 `` 的 `value` 属性**不稳定触发**(同文件 `` 却能取到), + * 导致 version 恒为 0、子游戏每次都误判需更新而重复下载。改用确定性正则直接从原文抽属性, + * 语义等价于 Android `pullxml1`,规避解析器回调时序/属性触发的不确定性。 */ -import { xml, util } from '@kit.ArkTS'; -import { Logger } from 'common'; - -/** version.xml 解析结果。 */ export interface VersionXmlInfo { /** 整数(缺失/失败 → 0)。 */ version: number; @@ -20,46 +20,36 @@ export interface VersionXmlInfo { } export class VersionXml { - private static readonly log: Logger = Logger.tag('VersionXml'); + // :取版本号。允许属性间任意顺序/空白。 + private static readonly VERSION_RE: RegExp = new RegExp(']*\\bvalue\\s*=\\s*"([^"]*)"', 'i'); + // 子 ...>:根 无属性(其后紧跟 '>'),故 \s+ 自然跳过,只命中带属性的子 game。 + private static readonly GAME_TAG_RE: RegExp = new RegExp(']*?>', 'i'); + private static readonly ID_RE: RegExp = new RegExp('\\bid\\s*=\\s*"([^"]*)"', 'i'); + private static readonly NAME_RE: RegExp = new RegExp('\\bname\\s*=\\s*"([^"]*)"', 'i'); - /** 解析 version + gameName。属性回调紧随其开始标签,故用 currentTag 把 name 归属到 game 标签。 */ + /** 解析 version + gameName + gameId(任一缺失取默认值,绝不抛错)。 */ static parse(xmlText: string): VersionXmlInfo { const info: VersionXmlInfo = { version: 0, gameName: '', gameId: '' }; if (xmlText.length === 0) { return info; } - let value: string = ''; - let currentTag: string = ''; - try { - const buf: Uint8Array = new util.TextEncoder().encodeInto(xmlText); - const parser: xml.XmlPullParser = new xml.XmlPullParser(buf.buffer as object as ArrayBuffer, 'UTF-8'); - const options: xml.ParseOptions = { - ignoreNameSpace: true, - tagValueCallbackFunction: (name: string, _val: string): boolean => { - currentTag = name; - return true; - }, - attributeValueCallbackFunction: (name: string, val: string): boolean => { - if (name === 'value') { - value = val; - } else if (name === 'name' && currentTag === 'game') { - // 根 无 name 属性;只有子 触发,正是所需游戏名 - info.gameName = val; - } else if (name === 'id' && currentTag === 'game') { - // 子 的 id:大厅 gameid 回退匹配 key(根 无 id 属性) - info.gameId = val; - } - return true; - }, - }; - parser.parseXml(options); - } catch (e) { - const err = e as Error; - VersionXml.log.w(`parse version.xml failed: ${err.message}`); - return info; + const verMatch: RegExpMatchArray | null = xmlText.match(VersionXml.VERSION_RE); + if (verMatch !== null) { + const n: number = parseInt(verMatch[1], 10); + info.version = isNaN(n) ? 0 : n; + } + const gameMatch: RegExpMatchArray | null = xmlText.match(VersionXml.GAME_TAG_RE); + if (gameMatch !== null) { + const tag: string = gameMatch[0]; + const idMatch: RegExpMatchArray | null = tag.match(VersionXml.ID_RE); + if (idMatch !== null) { + info.gameId = idMatch[1]; + } + const nameMatch: RegExpMatchArray | null = tag.match(VersionXml.NAME_RE); + if (nameMatch !== null) { + info.gameName = nameMatch[1]; + } } - const n: number = parseInt(value, 10); - info.version = isNaN(n) ? 0 : n; return info; }