Java Interop
Rhino provides a convenient way to interoperate with Java.
LiveConnect: Java communication from JavaScript
Rhino lets you create Java classes and call Java methods from JavaScript. For example:
let builder = new java.lang.Builder();
builder.append('test');
builder.append(1);
console.log(builder.toString());Accessing JavaBean properties
Java classes can define JavaBean properties using getters and setters. For example, the following class defines two properties:
public class Me {
public int getAge() \{ return mAge; \}
public void setAge(int anAge) \{ mAge = anAge; \}
public String getSex() \{ return "male"; \}
private int mAge;
};The two properties are age and sex. The sex property is read-only because it has no setter.
With Rhino, you can access Bean properties like JavaScript properties. You can also call the original getter/setter methods directly.
let me = new Me();
console.log(me.sex);
me.age = 33;
console.log(me.age);
console.log(me.getAge());Because sex is read-only, you cannot assign to it.
Importing Java classes and packages
Above we used importPackage to import all classes from a Java package. There is also importClass, which imports a single class.
You can use android.view.View directly to refer to Android's View class. The default supported top-level package prefixes are com, android, java, org. For other packages, use the Packages object, e.g. Packages.javax.xml.xpath.XPath or Packages["javax.xml.xpath.XPath"].
You can also use importClass or importPackage to import Java/Android packages or classes, for example:
importClass("android.view.KeyEvent");
// Or
let KeyEvent = android.view.KeyEvent;
// Or
importPackage("android.view");Extending Java classes & implementing Java interfaces in JavaScript
Example: set an OnClickListener for a UI widget:
"ui";
$ui.layout(
<frame>
<button id="btn" text="BUTTON"/>
</frame>
);
let listener = new android.view.View.OnClickListener(function(view) {
console.log("clicked");
});
$ui.btn.setOnClickListener(listener);When you write new android.view.View.OnClickListener(...), Rhino actually creates a new Java class that implements OnClickListener and forwards calls to the JavaScript object/function you provide.
Rhino also allows passing a JavaScript function directly to a Java method when the corresponding parameter is a Java interface with a single method (or all methods have the same number and types of parameters). For example:
$ui.btn.setOnClickListener(function(view) {
console.log("clicked");
});If a Java interface has multiple methods, you can pass a JavaScript object to implement it. For example, for the Java interface:
/**
* Interface definition for a callback to be invoked when this view is attached
* or detached from its window.
*/
public interface OnAttachStateChangeListener {
/**
* Called when the view is attached to a window.
* @param v The view that was attached
*/
public void onViewAttachedToWindow(View v);
/**
* Called when the view is detached from a window.
* @param v The view that was detached
*/
public void onViewDetachedFromWindow(View v);
}You can implement it in JavaScript like this:
let listener = new android.view.View.OnAttachStateChangeListener({
onViewAttachedToWindow: function(view) {
console.log('attached');
},
onViewDetachedFromWindow: function(view) {
console.log('detached');
}
});
$ui.btn.addOnAttachStateChangeListener(listener);JavaAdapter constructor
JavaAdapter can also be used to implement interfaces, and it can inherit from normal classes or abstract classes. Internally it generates a class dynamically.
let listener = new JavaAdapter(android.view.View.OnAttachStateChangeListener, {
onViewAttachedToWindow: function(view) {
console.log('attached');
},
onViewDetachedFromWindow: function(view) {
console.log('detached');
}
});If you want to implement multiple interfaces:
let listener = new JavaAdapter(android.view.View.OnAttachStateChangeListener, java.lang.Runnable, {
onViewAttachedToWindow: function(view) {
console.log('attached');
},
onViewDetachedFromWindow: function(view) {
console.log('detached');
},
run: function() {
console.log('run');
}
});In general, the syntax is:
new JavaAdapter(java-class, [java-class, ...], javascript-object, [args...])At most one java-class may be a Java class; the remaining java-class arguments are interfaces. The result inherits the specified Java class and implements all specified Java interfaces, forwarding any calls to methods on the javascript-object. The args are passed to the Java class constructor.
Example: inherit View and override onDraw:
"ui";
$ui.layout(
<vertical>
<frame id="container"/>
</vertical>
);
let paint = new Paint();
let view = new JavaAdapter(android.view.View, {
onDraw: function (canvas) {
// Call View's onDraw
this.super$onDraw(canvas);
canvas.drawRect(500, 500, 1000, 1000, paint);
},
// `activity` is a constructor argument for android.view.View
}, activity);
$ui.container.addView(view);