From c00f639ef744dbed3c737132dcc87c9d9a19a8c6 Mon Sep 17 00:00:00 2001 From: lanterngamescn Date: Fri, 26 Jun 2026 07:13:44 +0800 Subject: [PATCH] =?UTF-8?q?M5(T-M5-07):=20=E5=86=B7=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E2=80=94=E2=80=94=E8=BF=9C=E7=A8=8B=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=B8=8E=E5=86=85=E7=BD=AE=E8=B5=84=E6=BA=90=E5=87=86?= =?UTF-8?q?=E5=A4=87=E5=B9=B6=E8=A1=8C=E5=8C=96=20+=20=E8=80=97=E6=97=B6?= =?UTF-8?q?=E6=89=93=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FETCH_REMOTE_CONFIG(网络 1-5s) 与 prepareBuiltin(本地拷贝解压 0.2-0.8s) 无数据 依赖,改为同时进行;总耗时由 max(远程,内置) 决定而非求和(节省 ~内置耗时) - 各阶段加 [startup] 耗时打点(fetch_remote_config/prepare_builtin/update_game/total), 便于真机量化冷启动到首帧基线 - 行为变化注记:showmessage 阻断现发生在 prepareBuiltin 之后(并行所致),阻断时多做 一次幂等的内置准备;属罕见运营公告路径,无副作用,可接受 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../main/ets/startup/StartupOrchestrator.ets | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/domain_resource/src/main/ets/startup/StartupOrchestrator.ets b/domain_resource/src/main/ets/startup/StartupOrchestrator.ets index 23ad63f..5c334f5 100644 --- a/domain_resource/src/main/ets/startup/StartupOrchestrator.ets +++ b/domain_resource/src/main/ets/startup/StartupOrchestrator.ets @@ -58,8 +58,14 @@ export class StartupOrchestrator { } } + /** 冷启动耗时打点(T-M5-07 可观测,单位 ms)。 */ + private mark(label: string, since: number): void { + StartupOrchestrator.log.i(`[startup] ${label} +${Date.now() - since}ms`); + } + /** 执行启动流程。 */ async run(): Promise { + const t0: number = Date.now(); this.stage(StartupStage.INIT, 0); // 1) 本地配置 @@ -80,37 +86,53 @@ export class StartupOrchestrator { if (local.weburl !== '') { const entryUrl: string = `http://${local.weburl.replace(/-/g, '/')}?Launchtype=0`; this.stage(StartupStage.ENTER_HALL, 100); + this.mark('total(weburl)', t0); return { enter: true, entryUrl, message: '' }; } - // 3) 远程配置 + 4) 版本决策 - let decision: VersionDecision | undefined = undefined; - try { - this.stage(StartupStage.FETCH_REMOTE_CONFIG, 20); - decision = await config.resolve(); - this.stage(StartupStage.RESOLVE_VERSION, 35); - if (decision.showmessage !== '') { - StartupOrchestrator.log.w(`blocked by showmessage: ${decision.showmessage}`); - return { enter: false, entryUrl: '', message: decision.showmessage }; - } - } catch (e) { - const err = e as Error; - StartupOrchestrator.log.w(`remote config failed, fallback to local: ${err.message}`); - } + // T-M5-07 并行化:远程配置(网络 IO,1-5s)与内置资源准备(本地拷贝解压,0.2-0.8s) + // 无数据依赖——prepareBuiltin 只依赖本地 config,不依赖远程 decision。两者同时进行, + // 总耗时由 max(远程, 内置) 决定而非求和。 + this.stage(StartupStage.FETCH_REMOTE_CONFIG, 20); + const tRemote: number = Date.now(); + const remotePromise: Promise = + (async (): Promise => { + try { + const d: VersionDecision = await config.resolve(); + this.mark('fetch_remote_config', tRemote); + return d; + } catch (e) { + StartupOrchestrator.log.w(`remote config failed, fallback to local: ${(e as Error).message}`); + return undefined; + } + })(); - // 5) 资源准备 - this.stage(StartupStage.PREPARE_RESOURCE, 45); + // 内置资源准备(与上面远程请求并行推进) + this.stage(StartupStage.PREPARE_RESOURCE, 35); + const tBuiltin: number = Date.now(); if (!resource.isPrepared()) { await resource.prepareBuiltin(); } + this.mark('prepare_builtin', tBuiltin); + + // 汇合:取远程版本决策 + const decision: VersionDecision | undefined = await remotePromise; + this.stage(StartupStage.RESOLVE_VERSION, 55); + if (decision !== undefined && decision.showmessage !== '') { + StartupOrchestrator.log.w(`blocked by showmessage: ${decision.showmessage}`); + return { enter: false, entryUrl: '', message: decision.showmessage }; + } + + // 5) 版本更新(依赖内置资源已就绪 + 远程决策) const localVer: number = resource.localVersion(); if (decision !== undefined && decision.gameVersion > localVer && decision.gameDownload !== '') { StartupOrchestrator.log.i(`update needed: local=${localVer} remote=${decision.gameVersion}`); + const tUpdate: number = Date.now(); try { - await resource.updateGame(decision.gameDownload, (p: number) => this.stage(StartupStage.PREPARE_RESOURCE, 45 + Math.floor(p * 0.4))); + await resource.updateGame(decision.gameDownload, (p: number) => this.stage(StartupStage.PREPARE_RESOURCE, 55 + Math.floor(p * 0.3))); + this.mark('update_game', tUpdate); } catch (e) { - const err = e as Error; - StartupOrchestrator.log.w(`update failed, keep local: ${err.message}`); + StartupOrchestrator.log.w(`update failed, keep local: ${(e as Error).message}`); } } await resource.persistPaths(); @@ -121,12 +143,13 @@ export class StartupOrchestrator { } // 6) 注入 app_data.js(务必在进大厅之前)。大厅 launchtype='0' - this.stage(StartupStage.INJECT_APP_DATA, 90); + this.stage(StartupStage.INJECT_APP_DATA, 92); const values: AppDataValues = AppDataInjector.buildValues(local, `${resource.localVersion()}`, '0'); AppDataInjector.inject(resource.paths().gameDir, values); // 7) 进大厅 this.stage(StartupStage.ENTER_HALL, 100); + this.mark('total', t0); return { enter: true, entryUrl: `${resource.paths().indexUrl}?Launchtype=0`, message: '' }; } }