Cocos Creator 业务与原生通信详解

张开发
2026/4/9 22:10:19 15 分钟阅读

分享文章

Cocos Creator 业务与原生通信详解
在移动应用开发中越来越多的团队选择将 Cocos Creator 作为跨平台游戏/动画引擎嵌入原生 App如电商互动、营销小游戏、虚拟展厅等。此时业务层登录、支付、分享、数据上报等往往分布在原生端和 Cocos 两端如何实现稳定、高效的双向通信是保证项目顺利落地的关键。本文基于实际项目经验系统讲解 Cocos CreatorTypeScript与原生平台Android/iOS的业务层交互方案并提供可直接落地的代码实践。一、背景与需求典型混合架构中原生 App 负责提供系统能力、账号体系、支付渠道等核心业务Cocos 负责渲染互动场景、执行游戏逻辑。交互场景通常包括Cocos 调用原生获取设备信息、调用分享/支付、请求网络数据、触发震动等。原生调用 Cocos用户登录成功后通知 Cocos 更新 UI、推送消息到达、支付回调结果等。因此需要一套双向、异步、可靠的通信机制同时兼顾易用性与可维护性。二、技术选型与通信原理2.1 Cocos Creator 与原生通信基础Cocos Creator 底层基于 JavaScriptCoreiOS或 V8Android执行 JS/TS 代码原生代码可以通过 JSBJavaScript Binding桥接调用 JS 函数反之 JS 也能通过反射或桥接模块调用原生方法。版本原生调用 JS 方式JS 调用原生方式Cocos Creator 2.xCocosJavascriptJavaBridge.evalString(Android)[[AppController share] sendEventToCocos](iOS)jsb.reflection.callStaticMethodCocos Creator 3.xJsbBridge.sendToScriptJsbBridge.sendToNative或jsb.reflection兼容推荐使用JsbBridgeCocos 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: Mapstring, ((data: any) void)[] new Map(); private pendingCalls: Mapstring, 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): Promiseany { 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): Promisevoid { 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 iOS5.1 Android 实现5.1.1 初始化 JsbBridgeCocos 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{JSONObjectobjnewJSONObject(jsonMsg);Stringcmdobj.getString(cmd);StringcallbackIdobj.getString(callbackId);JSONObjectdataobj.optJSONObject(data);switch(cmd){casepay:// 调用支付 SDKdoPay(data,callbackId);break;casegetDeviceInfo:sendResponse(callbackId,getDeviceInfo());break;default:sendError(callbackId,unknown cmd);}}catch(Exceptione){e.printStackTrace();}}privatevoiddoPay(JSONObjectdata,StringcallbackId){// 模拟异步支付newThread(()-{// 支付结果booleansuccesstrue;JSONObjectresultnewJSONObject();try{result.put(orderId,123456);if(success){sendResponse(callbackId,result);}else{sendError(callbackId,pay failed);}}catch(Exceptione){}}).start();}privatevoidsendResponse(StringcallbackId,JSONObjectdata){JSONObjectrespnewJSONObject();try{resp.put(type,response);resp.put(callbackId,callbackId);resp.put(data,data);// 必须在主线程调用 CocosrunOnUiThread(()-JsbBridge.sendToScript(resp.toString()));}catch(Exceptione){}}privatevoidsendError(StringcallbackId,StringerrorMsg){JSONObjectrespnewJSONObject();try{resp.put(type,response);resp.put(callbackId,callbackId);resp.put(error,errorMsg);runOnUiThread(()-JsbBridge.sendToScript(resp.toString()));}catch(Exceptione){}}// 原生主动调用 Cocos例如登录成功后privatevoidnotifyLoginSuccess(StringuserId){JSONObjectmsgnewJSONObject();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#importplatform/ios/CCEAGLView-ios.h#importplatform/ios/CCRuntime.h#importjsb_bridge.hppimplementationAppController-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions{// 设置接收 Cocos 消息的回调se::JsbBridge::setCallback([](constse::JsbBridge::StringTypearg){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*cmddict[cmd];NSString*callbackIddict[callbackId];NSDictionary*paramsdict[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 successYES;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 通信协议规范定义清晰的消息清单如pay、share、getUserInfo避免随意扩展。所有回调必须携带callbackId超时机制防止永久等待。错误信息应结构化包含code和message。6.2 性能与安全避免频繁大字符串传递图片、日志等大数据通过文件或数据库共享消息只传路径。敏感信息加密如用户 token避免明文在桥接层泄露。防重放攻击callbackId 需包含时间戳和随机数。6.3 生命周期管理Cocos 场景切换时及时移除不需要的事件监听。原生端持有 Activity 上下文时注意弱引用避免内存泄漏。游戏处于后台时原生可缓存待发送消息恢复时再处理。6.4 调试技巧在 Cocos 端打印所有发送/接收的 JSON 日志并用JSON.stringify格式化。原生端使用Log.d记录桥接消息方便排查序列化错误。提供 mock 模式在没有原生环境时用 Web 模拟回调。6.5 异步处理与线程原生调用sendToScript必须在主线程AndroidrunOnUiThreadiOSdispatch_get_main_queue。Cocos 调用原生后原生耗时操作应在子线程执行结果再回调回主线程发送。七、总结本文介绍了一套基于JsbBridge和统一消息协议的原生与 Cocos Creator 业务层交互方案涵盖接口封装、异步 Promise 化、双端实现及工程实践要点。该方案已在多个生产项目中稳定运行支持登录、支付、分享等复杂业务场景。

更多文章