Interact with Java

October 22, 2022

Interact with Java

Rhino provides the ability to interact with Java very easily.

liveConnect: Java Communication with JavaScript

Rhino allows you to create Java classes and call Java methods from JavaScript. E.g:

let builder = new java.lang.Builder();
builder.append('test');
builder.append(1);
console.log(builder.toString());

Access JavaBean properties

Java classes can define JavaBean properties using getter and setter methods. 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 attributes defined are age and sex. The sex property is read-only: it has no Setter.

Using Rhino we can access Bean properties just like they are JavaScript properties. We can also go ahead and call methods that define properties.

let me = new Me();
console.log(me.sex);
me.age = 33;
console. log(me.age);
console.log(me.getAge());

Since the sex property is read-only, we are not allowed to write to it.

Import Java classes and packages

Above we saw the use of importPackage function to import all classes from a particular Java package. There is also importClass, which imports a single class.

You can directly use android.view.View to represent the View class in Android. The top-level package names supported by default are prefixed with com, android, java, org. For other package names, you need to use Packages object, such as Packages.javax.xml.xpath.XPath or Packages["javax.xml.xpath.XPath"].

You can also use the importClass or importPackage function to import package names or classes in Java/Android, such as:

importClass("android. view. KeyEvent");
// or
let KeyEvent = android.view.KeyEvent;
// or
importPackage("android. view");

Extend Java classes and implement Java interfaces using JavaScript

For example, set the click listener OnClickListener for a control in a certain UI:

"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 we type new android.view.View.OnClickListener, rhino actually creates a new Java class that implements OnClickListener and will forward calls from that class to the JavaScript object.

Rhino also allows JavaScript functions to be passed directly to Java methods, if the corresponding parameters are a Java interface, which has a single method or all of its methods have the same number of parameters, and the corresponding parameters have parameters of the same type. E.g:

$ui.btn.setOnClickListener(function(view) {
   console.log("clicked");
});

If the Java interface has multiple methods, you can pass in 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);
}

We can do this 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

Using JavaAdapter can also be used to implement interfaces, and can also be used to inherit ordinary or abstract classes. His principle is to dynamically generate a class.

let listener = new JavaAdapter(android.view.View.OnAttachStateChangeListener, {
   onViewAttachedToWindow: function(view) {
     console.log('attached');
   },
   onViewDetachedFromWindow: function(view) {
     console.log('detached');
   }
});

If we want to implement multiple interfaces then:

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 is a java class, and the remaining java-class parameters are interfaces. The result will be one that inherits the specified Java class and implements all Java interfaces, forwarding any calls to javascript-object's methods. The args parameter is used to specify the constructor that constructs the Java class.

For example, inherit View and rewrite the onDraw function:

"ui";

$ui.layout(
   <vertical>
     <frame id="container"/>
   </vertical>
);

let paint = new Paint();
let view = new JavaAdapter(android.view.View, {
   onDraw: function (canvas) {
     // Call the onDraw of the parent class View
     this. super$onDraw(canvas);
     canvas. drawRect(500, 500, 1000, 1000, paint);
   },
   // activity is the constructor parameter of android.view.View
}, activity);

$ui.container.addView(view);
Last update:
Contributors: Bruce