Registering Java Callbacks with J2V8

Registering Java Callbacks with J2V8

J2V8 is a set of Java bindings for Google’s popular JavaScript engine, V8. It was developed to bring highly efficient JavaScript to Android and is the workhorse behind Tabris.js. J2V8 also runs on Windows, Linux and Mac OS.  In the previous tutorial we looked at how to execute JavaScript using J2V8. In this tutorial we will demonstrate how to register Java callbacks with J2V8. Java callbacks allow JavaScript to invoke Java methods.

The Callback

In JavaScript, functions are first-class objects, i.e. they are objects and can be manipulated and passed around just like any other object. With J2V8, any JavaScript function can be mapped to a Java method. When the function is invoked, J2V8 will call the Java method instead, passing the JS arguments to Java.

Registering Java Methods

Java methods can be registered as JS callbacks in two different ways. You can either implement the JavaCallback interface (or JavaVoidCallback if the method doesn’t return a value) or you can register the method reflectively by specifying its signature.

JavaCallback

<pre lang="java">
 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');");
</pre>

In this example, we created an anonymous class that implements JavaVoidCallback. An instance of this class was registered with the J2V8 global scope and given the name print. Any JavaScript that is executed can now call print the same way any other JS Function is called.

Registering Methods Reflectively

<pre lang="java">
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');");
}
</pre>

In this example, a method of an existing object was registered reflectively. The Java object, the name of the method, and the parameter list must be specified.

In the first example, the Java method was registered on the V8 runtime itself. This puts the function in the global scope. In the second example, the methods were registered on an existing JavaScript object (named console).

Arguments

Arguments can be passed from JavaScript to Java methods. If the method was registered by implementing JavaCallback, then the arguments are passed as a V8Array. The V8Array contains the V8Objects (or primitives). The V8Array itself doesn’t need to be released because it was not instantiated by the developer. However, any V8Objects retrieved from the parameter list must be released because they were returned to you as a result of a method call.

If the method was registered reflectively, then the parameter types are known. In this case, the arguments passed to the JS function must match the Java method signature.

The Receiver

The JavaScript object on which the function was called is passed as the first parameter. Consider the following JavaScript:

<pre lang="javascript">
var array1 = [{first:'Ian'}, {first:'Jordi'}, {first:'Holger'}];
for ( var i = 0; i < array1.length; i++ ) {
  print.call(array1[i], " says Hi.");
}
</pre>

In this case, the print method will be invoked and ” says Hi.” will be passed in the parameter array. However, the actual JS Object is also important here. The V8Object will be passed as the receiver.
<pre lang="java">
class PersonPrinter implements JavaVoidCallback {
 @Override
 public void invoke(final V8Object receiver, final V8Array parameters) {
   System.out.println(receiver.getString("first") + parameters.get(0));
 }
}
</pre>

The V8Function Object

With J2V8 3.0, a V8Function object was introduced. A V8Function is a subclass of V8Object, and is returned whenever a function is returned from a getObject call. The V8Function has a call method, which can be used to invoke the function from Java.

Summary

In this tutorial we looked at how Java methods can be registered and invoked from JavaScript using J2V8. For more information on J2V8, follow me on Twitter.

No Comments

Sorry, the comment form is closed at this time.