M1: 容器接桥 + V1 阻塞性验证通过(T-M1-05/06/07)

- BridgeGameContainer 挂 Web + 接桥:onControllerAttached 建桥/注册 handler、
  onLoadIntercept 接桥、onPageEnd 注桥 JS + 补发启动队列、aboutToDisappear 释放
- test_echo.html 最小回显 H5(getTime 往返 + nativeEcho 特殊字符转义往返)
- 修复:dispatch 出站补转义单引号——lzyzsd 原版遗漏,_handleMessageFromNative('...')
  单引号包裹下 data 含 ' 会破坏字符串;补转义对 H5 透明(JSON.parse 仍得原始 data)
- ⚠ V1 真机通过:yy:// 被 onLoadIntercept 捕获、_fetchQueue 经 yy://return 回传、
  双向回执、含 "\/'中文 特殊字符转义往返一致。技术路线成立,无需回退

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lanterngamescn
2026-06-25 09:01:35 +08:00
co-authored by Claude Opus 4.8
parent 206db9730a
commit 3e2b1282ba
4 changed files with 145 additions and 51 deletions
@@ -1,42 +1,74 @@
import { RouteName, BridgeGameParams, GenericWebParams, GenericWebResult } from '../routes/AppRoutes';
import { webview } from '@kit.ArkWeb';
import { BridgeController, WebviewControllerAdapter, BridgeJsLoader } from 'feature_bridge';
import { BridgeGameParams } from '../routes/AppRoutes';
/**
* 大厅/子游戏容器(对应 webviewActivity,框架 §7.1)。
* M0:仅路由壳,演示打开通用网页容器并接收 101 回传。
* M2:填入 Web 组件 + BridgeController(桥协议 + §8/§9 handler)。
* 大厅/子游戏容器(对应 webviewActivity,框架 §7.1)。
* M1:挂 Web + 接桥(onControllerAttached 建桥 / onLoadIntercept 接桥 / onPageEnd 注桥),
* 加载最小回显 H5 验证 JsBridge 协议与 V1yy:// 拦截)。
* M2:入口 URL 改为大厅 file://.../gamehall/index.htmlM3:注册全部能力 Provider。
*/
@Component
export struct BridgeGameContainer {
pathStack: NavPathStack = new NavPathStack();
params: BridgeGameParams = { entryUrl: '', launchType: '0' };
@State private lastWebResult: string = '(无)';
private controller: webview.WebviewController = new webview.WebviewController();
private adapter: WebviewControllerAdapter | undefined = undefined;
private bridge: BridgeController | undefined = undefined;
/** 打开通用网页容器(§11.2 路径 B),onPop 接收子页回传(结果码 101 语义)。 */
private openGenericWeb(): void {
const params: GenericWebParams = { url: '', title: '活动页', data: 'hello', orientation: '0' };
this.pathStack.pushPathByName(RouteName.GENERIC_WEB, params, (popInfo: PopInfo) => {
const result = popInfo.result as GenericWebResult;
this.lastWebResult = result !== undefined ? result.data : '(空)';
}, false);
aboutToAppear(): void {
// 开发期开启远程调试;M5 加固时用 BuildProfile.DEBUG 守卫、release 必关(附录 B
webview.WebviewController.setWebDebuggingAccess(true);
}
aboutToDisappear(): void {
const a = this.adapter;
if (a !== undefined) {
a.dispose();
}
}
private setupBridge(): void {
const adapter = new WebviewControllerAdapter(this.controller);
const bridge = new BridgeController(adapter);
bridge.setBridgeJs(BridgeJsLoader.load(getContext(this)));
// 注册最小回显所需的原生 handler(M3 替换为全部能力 Provider
bridge.registerHandler('getTime', (_data: string, cb: (resp: string) => void) => {
cb(Date.now().toString());
});
bridge.registerHandler('nativeEcho', (data: string, cb: (resp: string) => void) => {
cb(data);
});
this.adapter = adapter;
this.bridge = bridge;
}
build() {
NavDestination() {
Column({ space: 16 }) {
Text('BridgeGameContainer 壳')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`launchType=${this.params.launchType}`)
.fontSize(14)
.fontColor(Color.Gray)
Text(`通用网页回传:${this.lastWebResult}`)
.fontSize(14)
Button('打开通用网页(测回传)')
.onClick(() => this.openGenericWeb())
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
Web({ src: $rawfile('test_echo.html'), controller: this.controller })
.javaScriptAccess(true)
.domStorageAccess(true)
.fileAccess(true)
.mixedMode(MixedMode.All)
.cacheMode(CacheMode.None)
.geolocationAccess(true)
.zoomAccess(false)
.width('100%')
.height('100%')
.onControllerAttached(() => {
this.setupBridge();
})
.onLoadIntercept((event: OnLoadInterceptEvent) => {
const url: string = event.data.getRequestUrl();
const b = this.bridge;
return b !== undefined ? b.onLoadIntercept(url) : false;
})
.onPageEnd(() => {
const b = this.bridge;
if (b !== undefined) {
b.onPageEnd();
}
})
}
.hideTitleBar(true)
}