#Cocos Creator# 快速实现JavaScript/TypeScript与Java代码相互调用
问题背景
我们经常需要从JavaScript或者TypeScript代码中调用第三方库的接口,这些接口,可能是一些第三方插件,比如广告,统计,或者GooglePlay,或者其他渠道的SDK。
在安卓平台上,要调用第三方库,就需要通过JS/TS与Java代码进行交互。
解决方案
1). 使用 JavaScript Binding
最复杂也最灵活的方案是,是自己写jsb代码,但是这样会非常麻烦,不信可以自己Google一下 JavaScript Binding.
2). 使用 Cocos Creator的jsb宏
Cocos Creator 提供了一套自己的jsb架构,并提供了一些宏来抹平不同底层的区别,具体参考:
https://docs.cocos.com/creator/manual/zh/advanced-topics/jsb/JSB2.0-learning.html
这套接口很多也相对完备,不过挺复杂的,细节很多。
3). 使用 Cocos Creator的jsb接口
另外,Cocos Creator已经基于自己的jsb实现,提供了一些快速的接口供我们是用。
官方文档 《如何在 Android 平台上使用 JavaScript 直接调用 Java 方法》
https://docs.cocos.com/creator/manual/zh/advanced-topics/java-reflection.html
使用Cocos Creator的jsb与Java交互的步骤
1. JavaScript 调用 Java接口
使用 jsb.reflection.callStaticMethod 可以访问指定Java类中的静态方法
var o = jsb.reflection.callStaticMethod(className, methodName, methodSignature, parameters...)
1) 参数
-
className
Java的完整类名,与Java中引用不一样的地方是,需要.替换成/,例如:
org/cocos2dx/javascript/tpcl/admobHelper -
methodName
Java类中的静态方法名 -
methodSignature
要调用的方法的参数和返回值描述
签名的格式:(参数1参数2参数3)返回值
参数列表放在()之间,并且参数之间不要有空格和任何符号
返回值跟在()后边。
签名举例
()V 表示没有参数,没有返回值的方法,即 void foo()
(I)V 表示参数为一个int,没有返回值的方法 void foo(int a)
(I)I 表示参数为一个int,返回值为int的方法 int foo(int a)
(IF)Z 表示参数为一个int和一个float,返回值为boolean的方法 boolean foo(int a, float b)
- parameters
传入的参数值
2) 返回值
只要是支持的类型,可以直接使用。
3) 注意事项
支持类型
目前 Cocos Creator 中支持的 Java 类型签名有下面 4 种:
也就是说,要使用Cocos Creator的jsb方法,只能用最基础的int/float/bool/string类型,不支持其他复杂类型,比如自定义对象,回调方法等。
UI线程和GL线程
Android的App中是区分UI线程和GL线程的,不同的接口需要在不同的线程中调用。
其中,cocos 引擎的渲染和 JS 的逻辑是在 GL 线程中进行的,而 Android 本身的 UI 更新是在 App 的 UI 线程进行的,所以如果我们在 JS 中调用的 Java 方法有任何刷新 UI 的操作,都需要在 UI 线程进行。
从JavaScript中直接调用Java方法时,默认是在GL线程。
如果不知道哪些方法不适合在GL线程中调用,可以使用默认调用方式,在调试的时候,Android Studio会提示你,这时候可以切换到UI线程中调用:
通过调用 Cocos2dxActivity.runOnUiThread 方法可以在切换到UI线程。
以Admob为例,Admob中大部分方法都需要在UI线程中调用。
public static void showBanner(){
System.out.println("[admob.Banner.show]");
_app.runOnUiThread(new Runnable() {
@Override
public void run() {
_banner.setVisibility(View.VISIBLE);
updateBannerPosition();
_banner.resume();
}
});
}
其中 _app是在初始化时传入的Cocos2dxActivity对象
public static void setContext(Cocos2dxActivity app, RelativeLayout layout){
_app = app;
_rootLayout = layout;
}
4) 示例
Java类
public class admobHelper {
public static void init(){
Log.d("admob", "init");
System.out.println("[admob.init]");
MobileAds.initialize(_app, APP_ID);
}
}
JavaScript/TypeScript
export default class AdmobHelper{
public static init(){
if(cc.sys.platform == cc.sys.IPHONE || cc.sys.platform == cc.sys.IPAD){
jsb.reflection.callStaticMethod("TpclHelper", "initAdmob");
}else if(cc.sys.platform == cc.sys.ANDROID){ jsb.reflection.callStaticMethod("org/cocos2dx/javascript/tpcl/admobHelper", "init", "()V");
}
}
2. Java 调用 JavaScript 接口
在少数情况下,我们还需要从Java中调用JavaScript接口。
使用 Cocos2dxJavascriptJavaBridge.evalString 可以执行一段JavaScript代码。
var o = jsb.reflection.callStaticMethod(className, methodName, methodSignature, parameters...)
值得注意的是,这个方法只有一个参数,就是要执行的JavaScript代码。它的实现,类似于在Chrome开发者工具中的Console中直接执行代码。
如果要调用Cocos引擎的方法,由于这些方法都在cc对象中,而cc已经提前由引擎暴露到了全局环境里,所以,可以直接用cc.xxx来调用。
Cocos2dxJavascriptJavaBridge.evalString("cc.log(\"Javascript Java bridge!\")");
而如果是自己的变量呢?同样的,你需要提前把你的对象暴露到全局环境里,可以使用window变量
假设,JavaScript接口
let foo = {
sayHello : function(){
console.info("hello");
}
}
window["foo"]=foo;
Java代码
app.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxJavascriptJavaBridge.evalString("foo.sayHello()");
}
});
1). 参数与返回值
-
怎么传参数
可以直接拼到evalString的参数里即可。 -
怎么返回值
貌似不支持返回值。
2). 注意事项
有些方法需要在GL线程中才能使用,所以需要用到Cocos2dxActivity.runOnGLThread
iOS平台怎么办?
iOS平台上,我们也可以利用Cocos Creator的jsb接口,来实现JavaScript与Object C的交互。
方法也是非常类似的,参考官方文档《如何在 iOS 平台上使用 Javascript 直接调用 Objective-C 方法》
https://docs.cocos.com/creator/manual/zh/advanced-topics/oc-reflection.html
这个很详细了,没有什么坑。
方法
使用 jsb.reflection.callStaticMethod 就可以调用指定类的静态方法了。
参数
分别是 className, methodName, paramter1, paramter2, ...
签名
OC的jsb不需要提供签名,但是需要在方法名中包含完整的定义。
举例
Object C定义
@interface NativeOcClass : NSObject
+(BOOL)callNativeUIWithTitle:(NSString *) title andContent:(NSString *)content;
@end
JavaScript
var ret = jsb.reflection.callStaticMethod(
"NativeOcClass", // 类名
"callNativeUIWithTitle:andContent:", // 方法名
"cocos2d-js", // 参数1 title
"Yes! you call a Native UI from Reflection" // 参数2 Content
);
注意事项
值得注意的是:
可能是由于苹果不支持动态执行脚本的缘故,Cocos Creator的jsb接口并不支持使用evalString方法,无法直接从Object C执行JavaScript脚本。
扫一扫关注微信公众号:耿直的IT男阿斌
聊一聊IT男眼中的世界