用J2V8注册Java回调函数

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方法

可以用两种不同的方式将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));
 }
}

V8Function对象

在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]

本文只做学习交流使用,原文版权归原作者所有,在未取得相关授权的情况下请勿用于任何其他用途, 若要转载,请保留原文以及译文出处

你可能感兴趣的:(Javascript,JAVA,Android开发)