CocosCreator 之 JavaScript/TypeScript和Java的相互交互_cocos 如何调用java
引擎版本: 3.8.1
语言: JavaScript/TypeScript、C++、Java
环境:Window
参考:Java原生反射机制
您好,我是鹤九日!
回顾
在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。
我们简单讲解了UnityAds跨平台接入,关于JavaScript/TypeScript和Java的交互,鉴于篇幅的缘故,只有寥寥数笔。
本篇文章的主题便是跨平台交互相关,主要内容:
一、JavaScript/TypeScript 调用 Java 接口
二、Java调用JavaScript
开始之前
正式开始之前,稍微对语言说明下,有助于我们对跨平台交互的理解!
Cocos引擎,无论是Cocos2d-x还是CocosCreator,在不同平台下使用的语言大致如下:
一、Cocos2d-x:C++、Lua、Java、Object-C
二、CocosCreator: C++、JavaScript、TypeScript、Java、Object-C
简单的理解:
一、C++构建游戏引擎的基础,可以想象下为什么求职面试必问C++?
二、Java、Object-C多数同SDK相关,算是第三方交互的延伸
三、Lua、JavaScript、TypeScript等等,这些脚本资源则划分到资源中,用于热更新等。
这样的理解有失偏颇,但我们只需懂得:Cocos引擎在跨平台交互方面,离不开C++语言的支撑。
注:可以理解为不同语言的交互:C++是纽带、桥梁!
我很早的时候写过一篇文章,讲解的是 cocos2d-x Android平台与Lua的交互!
里面其实就三点:
一、交互的方法通过:callStaticMethod
二、C++封装了LuaJavaBridge
的接口
三、交互的大致步骤:
-
Lua调用Java: Lua --> C++ --> Java
-
Java调用Lua: Java --> C++ --> Lua
如果放到CocosCreator中,相信您已经想到我想要表达什么了。
Creator跨平台封装
就如上面所言:Creator引擎对于JavaScript同Java、Object-C的交互同样是以C++为纽带进行封装的。
调用的方法接口一如既往的还是:callStaticMethod
注:这一点也体现出,为什么会有些人认为Creator是2dx的衍生版本,因为太相似了!
在引擎源码中搜索下,如下图:
我们可以看到这样的信息:
一、JavaScript同Java的交互,使用的类接口:JavaScriptJavaBridge
二、JavaScript同Object-C的交互,使用的类接口: JavaScriptObjCBridge
// C++封装的JavaScript和Java之间的桥接函数static bool JavaScriptJavaBridge_callStaticMethod(se::State &s) {...}// C++封装的JavaScript和Object-C之间的桥接函数static bool JavaScriptObjCBridge_callStaticMethod(se::State &s) {...}
相信到这里,你已经明白交互的流程。
TypeScript经过编译器转换为JavaScript,JavaScript调用C++封装的桥接函数,调用Java来实现SDK相关!
简言之:JavaScript–> C++ --> Java
CallStaticMethod
CallStaticMethod
的被封装在模块native
的reflection
中, 主要定义如下:
import { native } from \'cc\'; // 用于调用Java的静态方法var o = native.reflection.callStaticMethod( className, // string, Java的类名,以/分割,不能是. methodName, // string, Java类的方法名 methodSignature, // string, Java方法签名 parameters... // any, Java方法的参数);// 调用Objective-C的静态方法var result = native.reflection.callStaticMethod( className, methodName, arg1, arg2, ..... // 直接传参即可,无需签名);
调用的Java、Objective-C方法一定为静态方法,这样的好处有:
-
无需实例化,便可直接调用
-
便于组织和管理,不依赖于类的实例状态,减少依赖调用更快
-
兼容性高
注:最早期的cocos2d-x版本有非静态方法的使用,但废弃掉了!
以Android平台下的UnityAds的广告SDK为例,可以这样编写:
public showRewardAd() { // 检测平台是否为原生平台 if (!sys.isNative) return; if (sys.os == sys.OS.ANDROID) { // Andriod const className = \"com/cocos/demo/UnityAdsManager\"; const methodName = \"showRewardAd\"; const signature = \"()V\"; native.reflection.callStaticMethod(className, methodName, signature); } else if (sys.os == sys.OS.IOS) { // ios }}
建议:针对于SDK交互相关,可创建一个单例管理类NativeManger,方便维护处理!
Java的方法签名
使用callStaticMethod
,传入的参数注意:
一、Java的类名字符串,应以/
间隔,而不是.
二、方法名字符串,在java中为static
方法
三、签名和参数是相对应的,目前Cocos Creator支持的Java类型主要有:
int
I
float
F
boolean
Z
String
Ljava/lang/String;
;
签名参数,这一点很重要,原因在于Java支持函数重载。
使用签名是为了方便我们能够正确的调用到对应的方法。
签名参数的格式很简单:(参数类型)返回值类型
简单的示例:
const reflection = native.reflection;// \"()V\" 表示没有参数,没有返回值reflection.callStaticMethod(..., \"()V\");// \"(I)Z\" 表示单个整形参数,返回整形数值let value:boolean = reflection.callStaticMethod(..., \"(I)I\", 3);// \"(II)I\" 表示两个整形参数,返回整形数值let value:number = reflection.callStaticMethod(..., \"(II)I\", 3, 7);// \"(IILjava/lang/String;)Z\" 表示两个整形、一个字符串,返回布尔数值let value:boolean = reflection.callStaticMethod(..., \"(IILjava/lang/String;)Z\", 3, 7, \"str\");// \"(Ljava/lang/String;)Ljava/lang/String;\" 表示一个整形参数,返回字符串let value:string = reflection.callStaticMethod(..., \"(Ljava/lang/String;)Ljava/lang/String;\", \"content\");
注:如果需要传入复杂对象,可借助
JSON.stringify
转换为字符串
Java调用JavaScript
SDK的接入,使用callStaticMethod
并不能完全满足需求。比如:
支付的接入,没有反馈,我们不知道支付的成功或失败。
广告的播放,没有反馈,我们不知道广告能否正常播放、是否正常播放结束。
还好,CocosCreator引擎提供了Java调用JavaScript的机制。主要实现有两部分:
Java部分
Java部分主要有两个接口:
一、通过CocosHelper.runOnGameThread
获取执行中的GL线程。
注:GL 线程指的是引擎下JavaScript运行的特定线程,它负责处理渲染和JavaScript的逻辑。
为了确保线程安全,所有与 JavaScript 引擎交互的操作都必须在这个线程中执行;否则,无法保证线程安全。
二、在GL线程中执行CocosJavascriptJavaBridge.evalString
调用JavaScript中的脚本代码。
evalString
接口内的参数是JavaScript中的方法,但往往是window
环境全局对象下的方法。虽为字符串形式,但同样支持JavaScript参数的传递,只不过只支持string, number 和 bool三种基础类型。
简单的示例:
import com.cocos.lib.CocosHelper;import com.cocos.lib.CocosJavascriptJavaBridge;// 方式1: 使用匿名内部类实现,无参数传递CocosHelper.runOnGameThread(new Runnable() { @Override public void run() { CocosJavascriptJavaBridge.evalString(\"window.nativeEmpty()\"); }});// 方式2: 使用Lambda表达式,传递字符串、整数和布尔类型CocosHelper.runOnGameThread( () -> CocosJavascriptJavaBridge.evalString(\"window.nativeParam(\'test\',1,true)\"););
注:使用window全局对象,java中的\"window.callByNative()\" 也可为\"callByNative()\"
注:两种方式等价,只不过后者更简洁、易读些
JavaScript/TypeScript部分
Java部分要调用JavaScript的方法,必须保证对象绑定在window
环境全局对象中。主要原因有:
一、确保方法能够在全局作用域中访问
二、window
对象是JavaScript的全局命名空间,避免复杂项目的命名冲突
三、确保代码在不同JavaScript环境中具有良好的兼容性和可移植性。
对应Java部分,TypeScript处可以这样编写:
window.nativeEmpty = function(){ //to do}window.nativeParam = function(a:string, b:number, c:bool){ //to do}
当然了,也可以创建单例对象类,并借助window
注册为全局管理类。
export class NativeMgr{ private static _inst:NativeMgr; public static get inst():NativeMgr{ if(!this._inst){ this._inst = new NativeMgr(); } return this._inst; } public static nativeEmpty(){ //to do } public static nativeParam(a:string, b:number, c:bool){ //to do }}//将 NativeMgr 注册为全局类,否则无法在 Java 中被调用window.NativeMgr = NativeMgr;
使用管理类,在Java部分evalString
传递参数的时候,可以这样编写:
String jsCall = \"NativeMgr.inst.callByNative(\'test\',1,true)\";CocosHelper.runOnGameThread(() -> CocosJavascriptJavaBridge.evalString(jsCall));
UnityAds实战
这里呢,我们以UnityAds奖励广告的播放为例,它可能会出现这样的情况:
一、广告平台未初始化成功或不支持当前平台导致显示失败
二、广告为正常播放结束或者已经正常播放结束
Java中UnityAdsManager
部分:
import com.cocos.lib.CocosHelper;import com.cocos.lib.CocosJavascriptJavaBridge;// 显示奖励广告public static void showRewardAd() { getInstance().loadRewardAd();}// 加载显示奖励广告private void loadRewardAd() { boolean isSupport = isSupport(); boolean isInit = isInitialized(); if (!isSupport || !isInit) { String content = \"广告失败, isSupport: \" + isSupport + \" isInit:\" + isInit; String jsCall = \"NativeMgr.inst.onRewardAdFailed(\" + content + \")\"; CocosHelper.runOnGameThread( () -> CocosJavascriptJavaBridge.evalString(jsCall) ); return; } // 调用广告实例 this.rewardedAd.loadRewardedAd();}
Java中UnityAdsRewardedAd
监听广告显示完成部分:
public void onUnityAdsShowComplete(String placementId, UnityAds.UnityAdsShowCompletionState state) { if (state.equals(UnityAds.UnityAdsShowCompletionState.COMPLETED)) { // Reward the user for watching the ad to completion String jsCall = \"NativeMgr.inst.onRewardAdComplete(\" + true + \")\"; CocosHelper.runOnGameThread(() -> CocosJavascriptJavaBridge.evalString(jsCall)); } else { // Do not reward the user for skipping the ad String jsCall = \"NativeMgr.inst.onRewardAdComplete(\" + false + \")\"; CocosHelper.runOnGameThread(() -> CocosJavascriptJavaBridge.evalString(jsCall)); }}
JavaScript部分
export class NativeMgr{ private static _inst:NativeMgr; public static get inst():NativeMgr{ if(!this._inst){ this._inst = new NativeMgr(); } return this._inst; } public static onRewardAdFailed(err: string){ console.log(\"UnityAds Error:\", err); } public static onRewardAdComplete(sucess: boolean){ const content = sucess ? \"成功\" : \"失败\"; console.log(content); }}window.NativeMgr = NativeMgr;
最后
今天的文章到结束就结束了,可能理解有误,给您带来不便,期待您的反馈!
简单汇总下:
一、JavaScript同Java的交互,Cocos2dx和CocosCreator有相似之处,C++是纽带
二、JavaScript调用Java的静态方法使用的是callStaticMethod
,
三、callStaticMethod
参数支持int、float、boolean、string
类型,必须对应指定的(参数)返回值签名字符串
四、Java调用JavaScript方法,借助CocosHelper.runOnGameThread
运行到指定的GL流程中,然后再调用CocosJavascriptJavaBridge.evalString
五、evalString
调用的JavaScript对象必须在window全局环境中,参数传递支持int、bool、string
类型。
如果觉得文章不错,期待您的点赞和留言,感谢!
我是鹤九日,祝您生活愉快!