当前位置: 首页 > news >正文

Cocos Creator 业务与原生通信详解

在移动应用开发中,越来越多的团队选择将 Cocos Creator 作为跨平台游戏/动画引擎嵌入原生 App(如电商互动、营销小游戏、虚拟展厅等)。此时,业务层(登录、支付、分享、数据上报等)往往分布在原生端和 Cocos 两端,如何实现稳定、高效的双向通信,是保证项目顺利落地的关键。本文基于实际项目经验,系统讲解 Cocos Creator(TypeScript)与原生平台(Android/iOS)的业务层交互方案,并提供可直接落地的代码实践。

一、背景与需求

典型混合架构中,原生 App 负责提供系统能力、账号体系、支付渠道等核心业务,Cocos 负责渲染互动场景、执行游戏逻辑。交互场景通常包括:

  • Cocos 调用原生:获取设备信息、调用分享/支付、请求网络数据、触发震动等。
  • 原生调用 Cocos:用户登录成功后通知 Cocos 更新 UI、推送消息到达、支付回调结果等。

因此需要一套双向、异步、可靠的通信机制,同时兼顾易用性与可维护性。

二、技术选型与通信原理

2.1 Cocos Creator 与原生通信基础

Cocos Creator 底层基于 JavaScriptCore(iOS)或 V8(Android)执行 JS/TS 代码,原生代码可以通过 JSB(JavaScript Binding)桥接调用 JS 函数,反之 JS 也能通过反射或桥接模块调用原生方法。

版本原生调用 JS 方式JS 调用原生方式
Cocos Creator 2.xCocosJavascriptJavaBridge.evalString(Android)
[[AppController share] sendEventToCocos](iOS)
jsb.reflection.callStaticMethod
Cocos Creator 3.xJsbBridge.sendToScriptJsbBridge.sendToNativejsb.reflection(兼容)

推荐使用JsbBridge(Cocos 3.6+ 内置,低版本可手动移植),它提供了统一的双向字符串传递接口,便于封装成事件系统。

2.2 设计原则

  • 协议统一:所有通信消息采用 JSON 字符串,包含cmd(命令名)、data(载荷)、callbackId(可选,用于异步回调)。
  • 事件驱动:原生和 Cocos 分别维护事件监听器,通过on/off/emit模式解耦业务逻辑。
  • 线程安全:原生调用 Cocos 必须在主线程(UI 线程)执行;Cocos 调用原生可以异步执行耗时操作。

三、统一通信接口设计

我们在 TypeScript 层封装一个NativeBridge单例,对外提供callNative(method, params)onNativeEvent(eventName, callback)两个核心 API。

// NativeBridge.ts interface CallbackEntry { resolve: (data: any) => void; reject: (error: any) => void; timeoutId: number; } export class NativeBridge { private static instance: NativeBridge; private eventHandlers: Map<string, ((data: any) => void)[]> = new Map(); private pendingCalls: Map<string, CallbackEntry> = new Map(); private callbackId = 0; static getInstance(): NativeBridge { if (!this.instance) this.instance = new NativeBridge(); return this.instance; } // Cocos 调用原生(支持 Promise 异步回调) callNative(method: string, params?: any, timeout = 30000): Promise<any> { return new Promise((resolve, reject) => { const callbackId = `${method}_${Date.now()}_${this.callbackId++}`; const timeoutId = setTimeout(() => { this.pendingCalls.delete(callbackId); reject(new Error(`Call native method ${method} timeout`)); }, timeout); this.pendingCalls.set(callbackId, { resolve, reject, timeoutId }); const message = JSON.stringify({ cmd: method, data: params, callbackId, }); if (typeof (window as any).JsbBridge?.sendToNative === 'function') { (window as any).JsbBridge.sendToNative(message); } else if (typeof (window as any).jsb?.reflection?.callStaticMethod === 'function') { // 降级方案 (window as any).jsb.reflection.callStaticMethod( 'com/example/NativeHelper', 'call', '(Ljava/lang/String;)V', message ); } else { reject(new Error('Native bridge not available')); } }); } // 原生调用 Cocos 时触发的事件监听 onNativeEvent(eventName: string, callback: (data: any) => void): void { if (!this.eventHandlers.has(eventName)) { this.eventHandlers.set(eventName, []); } this.eventHandlers.get(eventName)!.push(callback); } offNativeEvent(eventName: string, callback?: (data: any) => void): void { if (!callback) { this.eventHandlers.delete(eventName); return; } const handlers = this.eventHandlers.get(eventName); if (handlers) { const index = handlers.indexOf(callback); if (index !== -1) handlers.splice(index, 1); } } // 由原生调用,分发事件或回调 public onNativeMessage(rawMessage: string): void { try { const msg = JSON.parse(rawMessage); if (msg.type === 'event') { const handlers = this.eventHandlers.get(msg.event); if (handlers) { handlers.forEach(handler => handler(msg.data)); } } else if (msg.type === 'response') { const pending = this.pendingCalls.get(msg.callbackId); if (pending) { clearTimeout(pending.timeoutId); if (msg.error) pending.reject(msg.error); else pending.resolve(msg.data); this.pendingCalls.delete(msg.callbackId); } } } catch (e) { console.error('[NativeBridge] parse error', e); } } }

四、Cocos 端业务使用示例

4.1 调用原生支付接口

import { NativeBridge } from './NativeBridge'; async function pay(productId: string): Promise<void> { try { const result = await NativeBridge.getInstance().callNative('pay', { productId, amount: 6.99, }); console.log('支付成功', result); // 更新游戏钻石 } catch (error) { console.error('支付失败', error); // 提示用户失败 } }

4.2 监听原生登录事件

// 在场景启动时注册监听 NativeBridge.getInstance().onNativeEvent('userLogin', (userInfo) => { console.log('收到登录信息', userInfo); // 更新游戏内用户头像、昵称等 }); // 页面销毁时移除监听(避免内存泄漏) onDestroy() { NativeBridge.getInstance().offNativeEvent('userLogin'); }

五、原生端实现(Android & iOS)

5.1 Android 实现

5.1.1 初始化 JsbBridge(Cocos 3.x 推荐)

Cocos 3.x 的JsbBridge在 Android 端对应JsbBridge.java,我们在AppActivity中初始化回调:

// AppActivity.javaimportcom.cocos.lib.JsbBridge;publicclassAppActivityextendsCocosActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);// 设置原生消息处理函数,接收 Cocos 发来的消息JsbBridge.setCallback(newJsbBridge.ICallback(){@OverridepublicvoidonScript(Stringmessage){handleFromCocos(message);}});}privatevoidhandleFromCocos(StringjsonMsg){try{JSONObjectobj=newJSONObject(jsonMsg);Stringcmd=obj.getString("cmd");StringcallbackId=obj.getString("callbackId");JSONObjectdata=obj.optJSONObject("data");switch(cmd){case"pay":// 调用支付 SDKdoPay(data,callbackId);break;case"getDeviceInfo":sendResponse(callbackId,getDeviceInfo());break;default:sendError(callbackId,"unknown cmd");}}catch(Exceptione){e.printStackTrace();}}privatevoiddoPay(JSONObjectdata,StringcallbackId){// 模拟异步支付newThread(()->{// 支付结果booleansuccess=true;JSONObjectresult=newJSONObject();try{result.put("orderId","123456");if(success){sendResponse(callbackId,result);}else{sendError(callbackId,"pay failed");}}catch(Exceptione){}}).start();}privatevoidsendResponse(StringcallbackId,JSONObjectdata){JSONObjectresp=newJSONObject();try{resp.put("type","response");resp.put("callbackId",callbackId);resp.put("data",data);// 必须在主线程调用 CocosrunOnUiThread(()->JsbBridge.sendToScript(resp.toString()));}catch(Exceptione){}}privatevoidsendError(StringcallbackId,StringerrorMsg){JSONObjectresp=newJSONObject();try{resp.put("type","response");resp.put("callbackId",callbackId);resp.put("error",errorMsg);runOnUiThread(()->JsbBridge.sendToScript(resp.toString()));}catch(Exceptione){}}// 原生主动调用 Cocos(例如登录成功后)privatevoidnotifyLoginSuccess(StringuserId){JSONObjectmsg=newJSONObject();try{msg.put("type","event");msg.put("event","userLogin");msg.put("data",newJSONObject().put("userId",userId));runOnUiThread(()->JsbBridge.sendToScript(msg.toString()));}catch(Exceptione){}}}

5.1.2 老版本或 jsb.reflection 方式(兼容)

若无法使用JsbBridge,可通过CocosHelper工具类:

publicclassCocosHelper{publicstaticvoidcallCocos(Stringmsg){CocosJavascriptJavaBridge.evalString("window.onNativeMessage && window.onNativeMessage('"+escape(msg)+"')");}}

注意转义特殊字符。

5.2 iOS 实现

iOS 中 Cocos 引擎通过CCRuntime执行 JS,原生可通过[[CCDirector sharedDirector] getVRoot]->_runtime->executeScript()调用 JS 函数。更简单的方式是利用JsbBridge

5.2.1 使用 JsbBridge

// AppController.mm#import"platform/ios/CCEAGLView-ios.h"#import"platform/ios/CCRuntime.h"#import"jsb_bridge.hpp"@implementationAppController-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions{// 设置接收 Cocos 消息的回调se::JsbBridge::setCallback([](constse::JsbBridge::StringType&arg){NSString*msg=[NSString stringWithUTF8String:arg.c_str()];[selfhandleFromCocos:msg];});returnYES;}-(void)handleFromCocos:(NSString*)jsonMsg{NSData*data=[jsonMsg dataUsingEncoding:NSUTF8StringEncoding];NSDictionary*dict=[NSJSONSerialization JSONObjectWithData:data options:0error:nil];NSString*cmd=dict[@"cmd"];NSString*callbackId=dict[@"callbackId"];NSDictionary*params=dict[@"data"];if([cmd isEqualToString:@"pay"]){[selfdoPay:params callbackId:callbackId];}}-(void)doPay:(NSDictionary*)params callbackId:(NSString*)callbackId{// 模拟异步dispatch_async(dispatch_get_global_queue(0,0),^{BOOL success=YES;NSDictionary*result=@{@"orderId":@"xxx"};[selfsendResponse:callbackId data:result error:success?nil:@"failed"];});}-(void)sendResponse:(NSString*)callbackId data:(NSDictionary*)data error:(NSString*)error{NSMutableDictionary*resp=[NSMutableDictionary dictionary];resp[@"type"]=@"response";resp[@"callbackId"]=callbackId;if(error){resp[@"error"]=error;}else{resp[@"data"]=data;}NSError*err;NSData*jsonData=[NSJSONSerialization dataWithJSONObject:resp options:0error:&err];if(jsonData){NSString*jsonString=[[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];// 必须在主线程调用dispatch_async(dispatch_get_main_queue(),^{se::JsbBridge::sendToScript([jsonString UTF8String]);});}}// 原生主动触发事件-(void)onUserLogin:(NSString*)userId{NSDictionary*event=@{@"type":@"event",@"event":@"userLogin",@"data":@{@"userId":userId}};NSData*jsonData=[NSJSONSerialization dataWithJSONObject:event options:0error:nil];NSString*jsonString=[[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];dispatch_async(dispatch_get_main_queue(),^{se::JsbBridge::sendToScript([jsonString UTF8String]);});}@end

六、最佳实践与注意事项

6.1 通信协议规范

  • 定义清晰的消息清单(如paysharegetUserInfo),避免随意扩展。
  • 所有回调必须携带callbackId,超时机制防止永久等待。
  • 错误信息应结构化,包含codemessage

6.2 性能与安全

  • 避免频繁大字符串传递:图片、日志等大数据通过文件或数据库共享,消息只传路径。
  • 敏感信息加密:如用户 token,避免明文在桥接层泄露。
  • 防重放攻击:callbackId 需包含时间戳和随机数。

6.3 生命周期管理

  • Cocos 场景切换时,及时移除不需要的事件监听。
  • 原生端持有 Activity 上下文时注意弱引用,避免内存泄漏。
  • 游戏处于后台时,原生可缓存待发送消息,恢复时再处理。

6.4 调试技巧

  • 在 Cocos 端打印所有发送/接收的 JSON 日志,并用JSON.stringify格式化。
  • 原生端使用Log.d记录桥接消息,方便排查序列化错误。
  • 提供 mock 模式,在没有原生环境时用 Web 模拟回调。

6.5 异步处理与线程

  • 原生调用sendToScript必须在主线程(AndroidrunOnUiThread,iOSdispatch_get_main_queue)。
  • Cocos 调用原生后,原生耗时操作应在子线程执行,结果再回调回主线程发送。

七、总结

本文介绍了一套基于JsbBridge和统一消息协议的原生与 Cocos Creator 业务层交互方案,涵盖接口封装、异步 Promise 化、双端实现及工程实践要点。该方案已在多个生产项目中稳定运行,支持登录、支付、分享等复杂业务场景。

http://www.jsqmd.com/news/614963/

相关文章:

  • 【技术解密】fastMRI核心技术:从原始数据到临床图像的完整链路
  • 2026电镀污泥尾气治理厂家如何应对技术升级与环保合规挑战?
  • 突破性Elsevier审稿状态追踪解决方案:自动化监控系统提升学术出版效率
  • 南京道尔斯特机架式PDU重新定义工业级安全电源管理新范式
  • IntelliJ IDEA社区版下载安装2026新(附安装包)
  • 喔去,litellm 竟然被投毒了,赶紧检查你的机器中招了没有送
  • NVIDIA Profile Inspector:深度优化显卡性能的游戏技术解决方案
  • HTML函数在老旧浏览器运行慢是硬件问题吗_软硬协同分析【教程】
  • 2026届学术党必备的五大降重复率助手推荐榜单
  • Fan Control风扇控制软件:从入门到精通的散热优化方案
  • Common Voice 语音数据集:从技术探索到实战应用的完整指南
  • 程序员副业变现全攻略:从技术到收入
  • 【高并发架构紧急升级】:Java 25虚拟线程已默认启用?3天内完成Tomcat/Netty/Reactor适配的实操手册
  • 小白友好:Local SDXL-Turbo极简使用教程,开箱即用无需复杂配置
  • linux——信号量
  • WinClaw实战教程 01|安全版OpenClaw从零部署:5分钟上手+全功能实测+避坑大全
  • 告别录屏!用FFmpeg+Git Bash一键下载m3u8视频(附完整命令)
  • 2026届毕业生推荐的六大AI辅助论文方案推荐榜单
  • 如何通过游戏决策辅助提升英雄联盟竞技表现?技术赋能的智能解决方案
  • OpenEMS:开源能源管理系统的架构解析与应用实践
  • 2026 铺路钢板厂家推荐排行榜:2 公分、3 公分铺路、不锈钢板优质商家盘点 - 海棠依旧大
  • C#怎么限制并发请求数_C#如何保护服务器接口【必备】
  • API 类别 - UI 核心
  • 2026届必备的降AI率平台推荐
  • OPCUA客户端UaExpert和S71500PLC通信使用详细介绍
  • 怎么批量文件搜索?搜索不到文件夹,搜索不到文件,批量搜索推荐
  • 2025最权威的AI写作网站横评
  • C 标准库 - `<ctype.h>`
  • 如何让 CSS Grid 自适应容器尺寸并保持固定宽高
  • 2026届学术党必备的十大AI论文工具实际效果