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: '' }; } }