J2V8是一套针对谷歌的V8 JavaScript引擎的Java绑定。J2V8的开发为Android平台带来了高效的Javascript的执行环境,taris.js 就是基于J2V8开发的。J2V8同时也可以运行在Windows、Linux、MacOS上。在上一个教程中我们已经学习了如何使用J2V8来执行Javascript脚本。在本教程中我们将演示如何用J2V8来注册Java回调函数。Java回调函数允许在Javascript中调用Java的方法。
在Javascript中,函数是“第一类对象”,也就是说,函数也是对象,像其他对象一样可以被操作,可以被传递。使用J2V8可以将任何Javascript函数映射到Java的方法之上,当这些函数被调用时,J2V8实际上调用的是Java的方法,同时会将Javascript函数的参数传递给被调用的Java方法。
可以用两种不同的方式将Java方法注册为Javascript回调函数。你可以选择实现JavaCallback
接口(或者是JavaVoidCallback
接口,如果这个函数不返回值的话),你也可以通过指明该函数签名来反射地将Java方法注册为Javascript回调函数。
JavaCallback
JavaVoidCallback callback = new JavaVoidCallback() {
public void invoke(final V8Object receiver, final V8Array parameters) {
if (parameters.length() > 0) {
Object arg1 = parameters.get(0);
System.out.println(arg1);
if (arg1 instanceof Releasable) {
((Releasable) arg1).release();
}
}
}
};
v8.registerJavaMethod(callback, "print");
v8.executeScript("print('hello, world');");
在上述的例子中,我们创建了一个实现了JavaVoidCallback
的匿名类。而此类的一个实例注册到了J2V8的全局作用域中,并且被命名为print
。现在所有的Javascript脚本都可以向调用其他Javascript函数那样调用print
了。
反射地注册函数
class Console {
public void log(final String message) {
System.out.println("[INFO] " + message);
}
public void error(final String message) {
System.out.println("[ERROR] " + message);
}
}
public void start() {
Console console = new Console();
V8Object v8Console = new V8Object(v8);
v8.add("console", v8Console);
v8Console.registerJavaMethod(console, "log", "log", new Class>[] { String.class });
v8Console.registerJavaMethod(console, "err", "err", new Class>[] { String.class });
v8Console.release();
v8.executeScript("console.log('hello, world');");
}
在上面这个例子中,一个现存对象中的方法被反射性地注册为一个Javascript回调函数。使用这种方法,在注册时必须明确指明Java对象,该对象需要被注册的方法以及该方法的所有的参数的类型的数组。
在第一个例子中,Java方法是直接注册到V8运行时环境本身上的,这就将一个函数放置在了一个全局作用域中。而在第二个例子中,那两个java方法是被注册到一个已经存在的名为“console”的Javascript对象上的。
参数可以从Javascript中传递到Java方法中。如果一个Java方法是通过实现JavaCallback注册的,那么Javascript中的参数列表是通过一个V8Array传递的。这个V8Array包含了一些V8Object对象或者一些原始数据类型。这个V8Array本身是不需要开发者手动去释放它的,因为它不是由开发者创建的,然而,任何从这个V8Array中获取的作为参数的V8Object都需要手动的进行释放,因为它们是作为方法调用的结果返回给你的。
如果方法是通过反射的方式进行注册的,那么这个方法的所有的参数类型都是已知的。在这种情形下,传递给Javascript函数的参数必须与作为它的回调函数的Java方法的签名一致。
被调用的Javascript函数所在的对象会作为第一个参数被传递。请看以下示例:
var array1 = [{first:'Ian'}, {first:'Jordi'}, {first:'Holger'}];
for ( var i = 0; i < array1.length; i++ ) {
print.call(array1[i], " says Hi.");
}
在这种情形下,print
函数会被调用,并且会被在参数数组中被传递到Java回调函数中。不过,在这里,真正的Javascript对象也是很重要的。而V8Object则会作为receiver
传递。
class PersonPrinter implements JavaVoidCallback {
@Override
public void invoke(final V8Object receiver, final V8Array parameters) {
System.out.println(receiver.getString("first") + parameters.get(0));
}
}
在J2V8 3.0版本中,引入了一种V8Function对象。V8Function对象是V8Object的子类,而且无论何时,只要一个函数通过是getObject()
调用被返回的话,那么返回的就是一个V8Function对象(译注:而一般的只是返回V8Object对象而已)。V8Function对象有一个call
方法,可以被用来从Java中调用Javascript的函数。
在本教程中,我们学习了如何用J2V8将Java方法注册为Javascript的回调函数,并且从Javascript中调用它。
本文大多数地方都采用直译,但是中文与英文之间的语言习惯的差异不可避免,技术文章也是如此,况且原文中很多概念和词句都是在一个隐含的上下文环境之中,如果直接翻译,那么就会显得非常突兀、晦涩,因此这种情况下不得不采用意译或者注释。不求“雅”,但力求“信”、“达”,已经力求准确,但奈何水平有限,难免会有所偏颇,望请海涵与指正,不甚感激。当然也十分欢迎各位来相互探讨研究。
另,以上全部代码已在Linux x86_64,部分代码在Android x86平台上进行测试,可以正常运行。
原文作者:Ian Bull
原文连接:http://eclipsesource.com/blogs/2015/06/06/registering-java-callbacks-with-j2v8/
译者:zyzz1995
联系方式:[email protected]本文只做学习交流使用,原文版权归原作者所有,在未取得相关授权的情况下请勿用于任何其他用途, 若要转载,请保留原文以及译文出处