news 2026/10/17 4:46:02

Cocos Creator 原生 WebView 嵌入与双向通信实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cocos Creator 原生 WebView 嵌入与双向通信实战

1. 为什么要在 Cocos Creator 里嵌入 WebView

做过小游戏或者互动应用的朋友大概率都遇到过这种需求:游戏主逻辑跑在 Cocos Creator 里,但某个模块——比如活动页、排行榜、用户协议、支付说明、甚至是内嵌的 H5 小游戏——用原生引擎写成本太高,用 Web 技术栈反而半天就能搞定。这时候最自然的想法就是:能不能在 Cocos 的场景里直接嵌一个浏览器窗口,把 H5 页面塞进去?

答案是可以的,而且这是 Cocos Creator 跨平台开发里一个相当实用的能力。所谓WebView 嵌入,本质上就是在原生层(Android 的WebView、iOS 的WKWebView)创建一个真正的系统浏览器组件,然后把它作为一个原生视图叠加在游戏渲染层之上,再通过桥接机制让 JS 层和原生层互相通信。它和引擎自带的cc.WebView组件不是一回事——后者在原生平台上其实也是走系统 WebView,但在 Web 平台上就是个 iframe,行为差异很大。

这个能力解决的核心问题是混合开发的边界问题:把适合 Web 做的交给 Web,把适合引擎做的交给引擎,两者通过消息通道协作。适合谁来参考?我建议是有一定 Cocos Creator 使用经验、同时懂一点 Android 或 iOS 原生开发的同学。如果你完全没碰过原生层,这篇文章也能让你看懂整体链路,但真正落地时最好找个原生同学配合,或者自己补一下 Java/Kotlin 和 Objective-C/Swift 的基础。

我前后在三个项目里用过这套方案,踩过的坑不算少,从最早的"WebView 盖不住、点不动"到后来的"键盘弹起把游戏顶飞",每一个都是血泪。下面我把整套思路、实现细节和避坑经验完整拆一遍,你可以直接抄作业。

2. 整体方案设计与技术选型思路

2.1 三种可选路径的取舍

在 Cocos Creator 里实现 WebView 嵌入,市面上大致有三条路,我先把它们摆出来对比,你再决定走哪条。

方案实现方式优点缺点适用场景
引擎自带 cc.WebView用引擎封装好的组件跨平台统一 API,写起来快原生层定制能力弱,通信受限,样式不可控简单展示型页面
原生自定义 WebView + 桥接自己写 Android/iOS 原生代码,暴露接口给 JS完全可控,通信灵活,性能好需要原生开发能力,双端各写一套复杂交互、需要深度定制
纯 H5 页面跳转直接打开系统浏览器或新页面零成本脱离游戏上下文,体验割裂外链、协议类页面

我个人的结论很明确:只要涉及 JS 与游戏逻辑的双向通信、或者需要控制 WebView 的位置大小和生命周期,就别用引擎自带的组件,直接上原生自定义方案。引擎自带的cc.WebView在 Web 平台上是 iframe,在原生平台上是系统 WebView,但它的 API 太薄,你没法精细控制,比如你想让 WebView 只占屏幕下半部分、想监听页面加载进度、想在页面里调用游戏的方法,它都做得很别扭。

2.2 桥接通信的设计原则

选定原生自定义方案后,核心就是设计 JS 和原生之间的通信桥。这里有个关键原则:通信要单向解耦,不要互相直接调用。

什么意思?就是 JS 层不应该直接去调原生的方法,原生也不应该直接去调 JS 的函数,而是通过一个统一的消息通道,双方都往通道里发消息、从通道里收消息。这样做的好处是:双端实现可以独立演进,加一个新消息类型不用改对方的代码结构,调试的时候也容易打日志。

具体到实现上,Android 侧通常用addJavascriptInterface注入一个 Java 对象给页面,页面通过window.xxx.postMessage()调用;iOS 侧用WKScriptMessageHandler接收页面消息,用evaluateJavaScript往页面里发消息。Cocos 的 JS 层则通过引擎提供的原生反射机制(native.reflection或jsb)来调用原生暴露的方法。

注意:addJavascriptInterface在 Android 4.2 以下有安全漏洞,虽然现在最低版本早就过了,但注入的对象方法一定要加@JavascriptInterface注解,否则高版本上调用无效,这个坑我见过不止一个同学踩。

2.3 渲染层级与触摸事件的处理

这是整个方案里最容易翻车的地方。WebView 是原生视图,它和游戏画面不在同一个渲染管线里。在 Android 上,Cocos 的 GLSurfaceView 和 WebView 是两个独立的 View,谁盖在谁上面取决于 View 树的添加顺序和 Z 轴顺序。在 iOS 上,Cocos 的视图和 WKWebView 也是兄弟关系,需要手动管理层级。

核心矛盾在于:WebView 显示的时候,游戏画面还在下面跑,但触摸事件会被 WebView 拦截。如果你希望 WebView 显示时游戏暂停响应触摸,那没问题;但如果你希望 WebView 只占一部分区域、其他区域还能操作游戏,那就需要精确控制 WebView 的 frame 和触摸区域。

我的做法是:WebView 显示时,在 JS 层用一个全屏遮罩拦截所有触摸,只把需要穿透的区域通过原生接口设置成 WebView 的实际大小。这样逻辑清晰,不会出现"点到了游戏按钮但 WebView 没反应"的诡异情况。

3. 核心细节解析与实操要点

3.1 Android 侧的关键实现

Android 侧的核心工作是创建一个 WebView,把它加到 Activity 的根布局上,并暴露通信接口。我先说几个关键点。

第一,WebView 必须加在主线程,这个不用多说,Android 的 View 操作都必须在 UI 线程。Cocos 的原生调用默认就在主线程,所以一般没问题,但如果你在子线程里触发显示,记得用runOnUiThread包一层。

第二,WebView 的布局参数要动态计算。Cocos 的游戏画面是全屏的,WebView 如果要占屏幕的一部分,你需要把游戏坐标系转换成 Android 的像素坐标系。这里涉及一个坑:Cocos 的设计分辨率、屏幕实际分辨率、Android 的 dp/px 三者不是一回事。我的经验是,在 JS 层算好比例,把 0 到 1 的相对坐标传给原生,原生再乘以实际屏幕宽高,这样双端都不用关心具体分辨率。

第三,WebView 的销毁要及时。很多同学显示完 WebView 就忘了销毁,结果内存一路涨。正确的做法是:隐藏时调用removeView并从父布局移除,必要时调用destroy()释放资源。如果只是隐藏,至少也要onPause暂停页面里的定时器和视频。

// 简化的 Android 侧核心逻辑示意 public class WebViewBridge { private WebView webView; private Activity activity; public void showWebView(final String url, final float x, final float y, final float width, final float height) { activity.runOnUiThread(new Runnable() { @Override public void run() { if (webView == null) { webView = new WebView(activity); WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); webView.addJavascriptInterface(new JsBridge(), "cocosBridge"); } // 根据相对坐标计算实际像素 int screenW = activity.getWindowManager() .getDefaultDisplay().getWidth(); int screenH = activity.getWindowManager() .getDefaultDisplay().getHeight(); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( (int)(width * screenW), (int)(height * screenH)); params.leftMargin = (int)(x * screenW); params.topMargin = (int)(y * screenH); webView.setLayoutParams(params); if (webView.getParent() == null) { activity.addContentView(webView, params); } webView.loadUrl(url); } }); } }

这段代码是示意,实际项目里我会把 WebView 的创建和显示拆开,创建时只初始化一次,显示时只调整布局和可见性,避免重复创建带来的闪烁。

3.2 iOS 侧的关键实现

iOS 侧用WKWebView,比 Android 的 WebView 性能好很多,但配置也更繁琐。几个关键点:

WKWebView 的创建必须在主线程,这个和 Android 一样。配置WKWebViewConfiguration时要开启 JS 执行,并且注册WKScriptMessageHandler来接收页面消息。注意,WKScriptMessageHandler会造成循环引用,记得在合适的时候移除,否则 WebView 不会释放。

iOS 的坐标系统是左上角原点,和 Android 一致,但要注意 Retina 屏幕的 scale 问题。Cocos 的视图和 WKWebView 都在同一个坐标系下,直接用UIScreen.main.bounds计算即可。

// 简化的 iOS 侧核心逻辑示意 - (void)showWebView:(NSString *)url x:(CGFloat)x y:(CGFloat)y width:(CGFloat)width height:(CGFloat)height { if (!self.webView) { WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; [config.userContentController addScriptMessageHandler:self name:@"cocosBridge"]; self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config]; } CGRect screen = [UIScreen mainScreen].bounds; self.webView.frame = CGRectMake(x * screen.size.width, y * screen.size.height, width * screen.size.width, height * screen.size.height); if (!self.webView.superview) { [[[UIApplication sharedApplication] keyWindow] addSubview:self.webView]; } [self.webView loadRequest:[NSURLRequest requestWithURL: [NSURL URLWithString:url]]]; }

提示:iOS 上 WKWebView 的键盘处理是个大坑。当页面里有输入框、键盘弹起时,系统会自动调整视图,可能把整个游戏画面顶上去。解决办法是监听键盘通知,手动把游戏视图的 frame 恢复回去,或者干脆在 WebView 显示时把游戏视图隐藏。

3.3 JS 层与原生层的通信封装

JS 层要做的事情有两件:调用原生方法显示/隐藏 WebView,以及接收原生转发过来的页面消息。

Cocos Creator 的原生反射调用,在 3.x 版本里用native.reflection.callStaticMethod(Android)和native.reflection.callStaticMethod(iOS 的类方法)。但更推荐的做法是在原生层封装一个统一的入口类,JS 只调这一个类的方法,这样双端接口一致,JS 层不用写平台判断。

// JS 层封装示意 const WebViewBridge = { show(url, x, y, width, height) { if (sys.isNative) { if (sys.platform === sys.Platform.ANDROID) { jsb.reflection.callStaticMethod( "com/example/WebViewBridge", "showWebView", "(Ljava/lang/String;FFFF)V", url, x, y, width, height ); } else if (sys.platform === sys.Platform.IOS) { jsb.reflection.callStaticMethod( "WebViewBridge", "showWebView:x:y:width:height:", url, x, y, width, height ); } } }, hide() { // 类似地调用原生的 hide 方法 } };

这里有个细节:Android 的反射调用签名必须严格匹配,(Ljava/lang/String;FFFF)V表示参数是 String 加四个 float,返回值 void。写错了会直接崩溃或者静默失败,调试起来很痛苦。我的建议是先在原生层写一个最简单的无参方法测试通路,通了再逐步加参数。

3.4 消息通道的双向设计

通信是双向的:游戏要告诉页面一些状态(比如用户 ID、当前关卡),页面也要告诉游戏一些事件(比如用户点了某个按钮、页面加载完成)。

我的设计是统一用 JSON 字符串传递消息,消息体里带一个type字段区分类型。这样双端解析逻辑一致,扩展新消息类型只需要加一个 type,不用改通道本身。

// 游戏发给页面的消息 const msg = JSON.stringify({ type: "userInfo", data: { uid: "12345" } }); // 通过原生接口注入到页面 nativeBridge.evaluateJs(`window.onGameMessage(${msg})`); // 页面发给游戏的消息(页面里调用) window.cocosBridge.postMessage(JSON.stringify({ type: "pageReady" }));

原生层收到页面消息后,再通过 Cocos 的事件机制转发给 JS 层。Cocos 3.x 里可以用native.bridge或者自定义的事件派发。我一般会在原生层维护一个消息队列,JS 层定时拉取或者原生主动推送,看具体场景。

注意:evaluateJavaScript在 iOS 上必须在主线程调用,而且如果页面还没加载完,调用会失败。稳妥的做法是等页面didFinishNavigation回调后再发消息,或者在页面里主动发一个pageReady消息,游戏收到后再发数据。

4. 完整实操流程与核心环节实现

4.1 环境准备与项目结构

先说环境。Cocos Creator 我用的是 3.8 版本,Android 用 Android Studio,iOS 用 Xcode。构建原生项目时,Cocos 会生成一个build/android和build/ios目录,我们的原生代码就加在这两个目录里。

但这里有个大坑:每次重新构建,Cocos 会覆盖 build 目录,你手写的原生代码会丢。解决办法有两个:一是把原生代码放在 Cocos 的native/engine模板目录里,构建时会自动拷贝;二是用 git 管理,构建后手动合并。我推荐第一种,虽然配置麻烦点,但一劳永逸。

具体做法是在项目根目录建一个native/engine/android和native/engine/ios目录,把自定义的 Java/OC 文件放进去,然后在构建配置里指定模板路径。Cocos 3.x 支持自定义构建模板,这个在官方文档里有说明,我这里不展开。

4.2 Android 侧完整实现步骤

第一步,在native/engine/android下创建WebViewBridge.java,放在合适的包名下。这个类要提供showWebView、hideWebView、evaluateJs三个静态方法。

第二步,showWebView里创建 WebView 并添加到 Activity 的根布局。这里要注意,Cocos 的 Activity 根布局可能不是 FrameLayout,你需要先找到合适的父容器。我的做法是用activity.findViewById(android.R.id.content)拿到内容根布局,它一定是 FrameLayout,直接往里加就行。

第三步,配置 WebView 的WebSettings。必须开启的项:setJavaScriptEnabled(true)、setDomStorageEnabled(true)(页面用 localStorage 时需要)、setAllowFileAccess(true)(加载本地文件时需要)。如果页面要播放视频,还要设置setMediaPlaybackRequiresUserGesture(false)。

第四步,注入 JS 接口。addJavascriptInterface(new JsBridge(), "cocosBridge"),JsBridge类里定义@JavascriptInterface标注的postMessage方法,收到消息后通过 Cocos 的机制转发。

第五步,处理返回键。WebView 显示时,用户按返回键应该先关闭 WebView 而不是退出游戏。这需要在 Activity 的onBackPressed里判断,如果 WebView 可见就先隐藏它。

@Override public void onBackPressed() { if (WebViewBridge.isVisible()) { WebViewBridge.hideWebView(); return; } super.onBackPressed(); }

4.3 iOS 侧完整实现步骤

iOS 侧稍微复杂一点,因为要处理WKScriptMessageHandler的循环引用和键盘问题。

第一步,创建一个WebViewBridge类,实现WKScriptMessageHandler协议。注意用弱引用持有自己,避免循环引用。

第二步,showWebView里创建 WKWebView 并添加到 keyWindow 上。这里要注意,Cocos 的视图控制器可能不是 keyWindow 的 rootViewController,直接加到 keyWindow 上是最稳妥的。

第三步,注册消息处理器。[config.userContentController addScriptMessageHandler:self name:@"cocosBridge"],然后在userContentController:didReceiveScriptMessage:里处理消息。

第四步,处理键盘。监听UIKeyboardWillShowNotification和UIKeyboardWillHideNotification,在键盘弹起时把游戏视图的 frame 恢复,或者把 WebView 的 frame 上移。

- (void)keyboardWillShow:(NSNotification *)notification { CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; // 把 WebView 上移,避免被键盘遮挡 CGRect frame = self.webView.frame; frame.origin.y -= keyboardFrame.size.height; self.webView.frame = frame; }

第五步,销毁时移除消息处理器。[config.userContentController removeScriptMessageHandlerForName:@"cocosBridge"],否则 WebView 不会释放,内存泄漏。

4.4 JS 层的调用与状态管理

JS 层我建议封装一个单例的WebViewManager,管理 WebView 的显示状态、消息队列和回调。

class WebViewManager { constructor() { this.isShowing = false; this.messageHandlers = {}; } show(url, rect) { if (this.isShowing) return; this.isShowing = true; // 显示遮罩,拦截游戏触摸 this.showMask(); WebViewBridge.show(url, rect.x, rect.y, rect.width, rect.height); } hide() { if (!this.isShowing) return; this.isShowing = false; this.hideMask(); WebViewBridge.hide(); } onMessage(type, handler) { this.messageHandlers[type] = handler; } dispatchMessage(jsonStr) { const msg = JSON.parse(jsonStr); const handler = this.messageHandlers[msg.type]; if (handler) handler(msg.data); } }

遮罩的作用是拦截游戏触摸,防止用户在 WebView 上操作时误触游戏。遮罩本身是一个全屏的cc.Node,挂一个BlockInputEvents组件即可。

4.5 参数计算与坐标转换

坐标转换是实操中最容易出错的地方。Cocos 的坐标系是左下角原点,Android 和 iOS 都是左上角原点,所以 Y 轴要翻转。

假设游戏设计分辨率是 960x640,实际屏幕是 1920x1080,你想让 WebView 占屏幕中间 80% 宽、60% 高,居中显示。那么:

  • 相对宽度 = 0.8,相对高度 = 0.6
  • 相对 X = (1 - 0.8) / 2 = 0.1
  • 相对 Y = (1 - 0.6) / 2 = 0.2

传给原生的就是(0.1, 0.2, 0.8, 0.6),原生再乘以实际屏幕宽高。这样双端都不用关心具体分辨率,适配性最好。

提示:如果 WebView 要贴底显示(比如底部弹窗),相对 Y 就是1 - 高度。如果贴顶,相对 Y 就是 0。这些计算在 JS 层做,原生只负责乘屏幕尺寸。

5. 常见问题与排查技巧实录

5.1 WebView 显示白屏或加载失败

这是最常见的问题,原因通常有三个:网络权限没加、URL 不对、页面本身报错。

Android 上,AndroidManifest.xml里必须加<uses-permission android:name="android.permission.INTERNET" />,这个漏了会直接白屏。iOS 上,如果加载的是 HTTP 页面,需要在Info.plist里配置NSAppTransportSecurity允许任意加载,否则会被系统拦截。

排查方法:先在原生层加日志,看onPageStarted和onPageFinished有没有回调。如果onPageStarted都没触发,说明 URL 或网络有问题;如果触发了但页面空白,说明页面本身有 JS 错误,用 Chrome 的chrome://inspect或者 Safari 的开发者工具连上去看控制台。

5.2 触摸事件穿透或无法点击

WebView 显示后,游戏按钮还能点到,或者 WebView 里的按钮点不动,都是触摸事件处理的问题。

游戏按钮还能点到,说明遮罩没生效或者遮罩层级不对。检查遮罩节点的zIndex是否高于其他 UI,以及BlockInputEvents组件是否挂上。

WebView 里的按钮点不动,通常是 WebView 的 frame 设置有问题,或者被其他原生视图遮挡。检查 WebView 的LayoutParams是否正确,以及是否被添加到了正确的父容器。

5.3 键盘弹起导致画面错乱

iOS 上这个问题特别明显。键盘弹起时,系统会自动调整 keyWindow 的 frame,导致整个游戏画面被顶上去。

解决办法是在键盘通知里手动恢复游戏视图的 frame。具体做法是:在keyboardWillShow里记录当前游戏视图的 frame,然后把 WebView 上移;在keyboardWillHide里把 WebView 移回原位。如果游戏视图也被顶了,同样需要恢复。

Android 上默认不会顶起整个画面,但如果 Activity 的windowSoftInputMode设置成了adjustResize,也会出现类似问题。改成adjustPan或者adjustNothing可以避免。

5.4 内存泄漏与 WebView 销毁

WebView 不销毁会导致内存持续增长,尤其是反复打开关闭的场景。

Android 上,隐藏 WebView 时要removeView,必要时destroy()。但注意,destroy()后 WebView 就不能再用了,下次显示要重新创建。我的做法是:如果只是临时隐藏,用setVisibility(GONE);如果确定不再用了,才destroy()。

iOS 上,WKWebView 的销毁要移除WKScriptMessageHandler,否则会循环引用。另外,如果页面里有视频或定时器,要在隐藏时暂停。

5.5 常见问题速查表

问题现象可能原因排查方向解决方案
白屏网络权限缺失检查 Manifest/Info.plist添加 INTERNET 权限
白屏URL 错误看 onPageStarted 回调修正 URL,加日志
白屏页面 JS 报错连开发者工具看控制台修复页面代码
触摸穿透遮罩未生效检查遮罩层级和组件挂 BlockInputEvents
按钮点不动frame 错误检查 LayoutParams修正坐标计算
键盘顶飞画面系统自动调整监听键盘通知手动恢复 frame
内存泄漏WebView 未销毁监控内存曲线removeView + destroy
消息收不到桥接未注册检查 addJavascriptInterface确认注解和名称

5.6 几个我踩过的独家坑

第一个坑:Android 上addJavascriptInterface注入的对象,方法名如果和系统方法重名,会被覆盖。我有个项目里注入的方法叫close,结果和 WebView 自带的close冲突,页面调用时行为诡异。后来改成postMessage就正常了。所以注入的方法名最好加前缀,比如cocos_。

第二个坑:iOS 上evaluateJavaScript在页面重定向时会丢失。如果页面加载后发生了 302 跳转,之前注入的 JS 上下文会失效。解决办法是监听didFinishNavigation,每次导航完成后重新注入。

第三个坑:WebView 的缓存会导致页面更新不生效。开发阶段经常遇到改了 H5 页面但 WebView 里还是旧版,这是因为缓存。可以在 URL 后面加时间戳,或者设置setCacheMode(LOAD_NO_CACHE)。但生产环境不要禁用缓存,否则每次加载都慢。

第四个坑:Cocos 的构建会覆盖原生代码。前面提过,一定要把原生代码放在模板目录里,或者用 git 管理。我有一次构建后忘了合并,调试了半天才发现代码被覆盖了。

6. 性能优化与进阶玩法

6.1 加载速度优化

WebView 的首次加载通常比较慢,尤其是页面资源多的时候。几个优化方向:

预创建 WebView。在游戏启动时就创建好 WebView 实例,只是不显示。这样真正需要显示时,只需要 loadUrl,省去了创建的开销。但注意,预创建会占用内存,低端机上要权衡。

预加载页面。如果知道用户大概率会打开某个页面,可以在后台先 loadUrl,显示时直接展示。这个适合活动页这种确定性的场景。

资源本地化。把 H5 页面的 JS、CSS、图片打包到本地,用file://协议加载,速度比网络加载快很多。但要注意跨域问题,以及本地文件的更新机制。

6.2 通信性能优化

频繁的 JS 与原生通信是有开销的,尤其是大量数据传输时。几个优化点:

批量发送。不要每条消息都单独发,攒一批一起发。比如游戏状态同步,可以每 100ms 发一次,而不是每次变化都发。

减少数据量。传 JSON 时只传必要字段,不要整个对象序列化。大数组考虑用二进制或者分片传输。

避免同步调用。evaluateJavaScript在 iOS 上是异步的,但 Android 的loadUrl("javascript:...")也是异步的。不要指望调用后立即拿到返回值,用回调或事件机制。

6.3 进阶:WebView 与游戏的深度协作

基础的双向通信做好后,可以玩一些更高级的:

游戏画面截图传给页面。用 Cocos 的camera渲染到 RenderTexture,转成 base64 传给页面显示。适合做分享图、成绩单。

页面控制游戏角色。页面里的按钮点击后,通过消息通道调用游戏的方法,控制角色移动或释放技能。适合做虚拟手柄、活动交互。

页面内嵌游戏小游戏。页面里再嵌一个 Cocos 的 Web 版小游戏,通过 postMessage 和主游戏通信。这个比较重,但技术上可行。

提示:深度协作时,消息协议要设计好版本号,方便后续扩展。我一般会在消息体里加一个v字段,页面和游戏都根据版本号做兼容处理。

7. 我个人在实际操作中的几点体会

这套方案我从最早用引擎自带组件,到后来自己写原生桥接,前后迭代了三四版。最大的体会是:不要试图用一套代码解决所有平台的问题。Android 和 iOS 的 WebView 行为差异很大,强行统一 API 只会让代码越来越乱。我的做法是 JS 层只定义接口,双端各自实现,接口一致但内部逻辑完全独立。

另一个体会是日志一定要打全。WebView 的问题往往出在原生层,JS 层看不到。我在原生层的每个关键节点都加了日志,显示、隐藏、加载开始、加载完成、收到消息、发送消息,全部打出来。出问题时看日志基本能定位到是哪一环断了。

还有一点,测试一定要在真机上做。模拟器和真机的 WebView 行为差异很大,尤其是键盘、触摸、性能这些。我早期在模拟器上测得好好的,一到真机就各种问题。现在我的习惯是,只要涉及原生功能,一律真机测试,模拟器只用来快速验证逻辑。

最后分享一个小技巧:如果 WebView 里的页面需要和游戏共享登录态,不要通过 URL 传 token,那样不安全也不优雅。正确的做法是页面加载完成后,游戏通过消息通道把 token 发给页面,页面存到内存里,后续请求带上。这样 token 不会出现在 URL 里,也不会被浏览器历史记录泄露。

这套方案后续还可以扩展的方向很多,比如支持多个 WebView 实例、支持 WebView 动画过渡、支持页面与游戏共享本地存储等。我目前正在做的是把 WebView 的显示隐藏做成一个可配置的转场动画,让体验更顺滑。如果你也在做类似的东西,欢迎交流踩坑经验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 4:45:43

跨癌种病理AI通用化:癌症感知注意力与去偏机制实践

病理AI这几年卷得厉害&#xff0c;但真正把模型拉到医院里跑一跑&#xff0c;很多人会遇上同一堵墙&#xff1a;单一癌种训练出来的模型&#xff0c;在自己的“主场”上看着还行&#xff0c;一跨癌种、一换科室&#xff0c;准确率坠崖式下跌。我们团队在做跨癌种病理AI通用化项…

作者头像 李华
网站建设 2026/10/17 4:45:29

基于知识图谱的农作物病虫害智能问答系统:从设计到实现

简介&#xff1a;这份资源是面向高校毕业设计、课程设计学习者与知识图谱入门者的农作物病虫害智能问答系统完整项目包&#xff0c;围绕知识图谱与KBQA问答系统展开&#xff0c;可用于理解从知识建模到问答落地的全流程。压缩包共673个文件&#xff0c;约4.9MB&#xff0c;以55…

作者头像 李华
网站建设 2026/10/17 4:44:29

Codex CLI实测:终端里的AI编程代理,从安装到实战全解析

Codex CLI这东西&#xff0c;最近在开发者圈子里讨论度确实高。我拿到手实测了一轮&#xff0c;发现它跟之前在网页上点来点去的ChatGPT编程方式完全不是一回事——它是真正跑在终端里的一个编程代理&#xff0c;能直接读你硬盘上的代码、改文件、执行命令&#xff0c;甚至帮你…

作者头像 李华
网站建设 2026/10/17 4:42:08

VC6.0下纯Win32实现PNG加载与Alpha透明混合

简介&#xff1a;本资源是面向VC6.0开发者的C图像处理实战方案&#xff0c;聚焦PNG格式加载与Alpha通道透明化渲染这一经典兼容性难题&#xff0c;适用于嵌入式界面开发、老旧系统维护及Windows GDI底层图形编程学习者。压缩包共70个文件&#xff0c;含34个头文件&#xff08;如…

作者头像 李华
网站建设 2026/10/17 4:42:07

L-Edit V11.1 标准单元版图设计:从图层定义到GDS导出的完整指南

简介&#xff1a;L-Edit V11.1 是一款面向集成电路版图设计与教学场景的 EDA 工具资源&#xff0c;适合微电子、集成电路相关专业的学生、教师及初学版图设计的工程师使用。资源为免安装版本&#xff0c;解压后即可直接运行&#xff0c;省去繁琐的配置流程&#xff0c;便于在课…

作者头像 李华
网站建设 2026/10/17 4:41:57

AI Agent CLI 安装升级全指南:9款主流工具命令速查与避坑

AI Agent 这两年的热度不用我多说&#xff0c;从写代码、跑终端、查资料到操作浏览器&#xff0c;几乎每个团队都在试。但真到落地那一步&#xff0c;第一个卡住大多数人的不是模型能力&#xff0c;而是命令行工具本身——装哪个、怎么装、装完怎么确认版本、升级时又踩了什么坑…

作者头像 李华