Implementing Native Methods
Java Native Interface (JNI) with CheerpJ
This guide explains how to implement Java native methods in JavaScript with CheerpJ.
On the desktop, Java native methods are usually written in C or C++ and called through the Java Native Interface (JNI). Pre-built native binaries cannot run in the browser, so CheerpJ lets you implement native methods in JavaScript instead, where they can use JavaScript libraries and any Web API the browser offers. Native code can also be compiled to WebAssembly; get in touch if your application needs it.
In Java, native methods are marked by the native keyword in their declaration. They are not implemented in Java, but in another language: with CheerpJ, JavaScript.
Steps to implement native methods in CheerpJ
To implement native methods in CheerpJ:
- Declare a native method in Java using the
nativekeyword. - Implement the native method in JavaScript.
- Pass the native function to CheerpJ.
Declaring native methods in Java
To declare a native method in Java, use the native keyword in the method declaration. The method is declared in the Java class but not implemented in Java. Its implementation is provided in JavaScript.
public class ClassName { // Native method declaration private native void methodName(param1, param2, ...);}Implementing native methods in JavaScript
To implement a native method in JavaScript, create an async function named after the Java method: Java_<fully-qualified-class-name>_<method-name>. For instance, if com.foo.Bar has a native method called baz, the function is called Java_com_foo_Bar_baz.
The JavaScript function should accept the following parameters:
- A
CJ3Libraryobjectlibas the first parameter, which gives access to the application’s Java classes and methods. Use it to call back into Java. selfas the second parameter: the Java object the native method was called on. Static native methods do not have this parameter.- The native method’s own parameters after these.
The function returns a value, or a Promise that resolves to a value. The syntax is:
async function Java_<fully-qualified-class-name>_<method-name>(lib, self, param1, param2, ...) { // Implementation}Handling static native methodsIf the native method is static, there is no self parameter: the method’s own parameters follow lib.
Initialising CheerpJ with the natives option
Pass the function to cheerpjInit as a property of the natives option. There are two ways to do this. The examples implement a static native method, so the function has no self parameter.
- Define the function inside the option
await cheerpjInit({ natives: { async Java_Example_nativeMethodName(lib, str) { // Implementation }, },});- Or pass a function defined earlier, by name
async function Java_Example_nativeMethodName(lib, str) { // Implementation}
await cheerpjInit({ natives: { Java_Example_nativeMethodName } });Example walkthrough
This full example declares a native method in Java and implements it in JavaScript.
- Declare a native method in Java using the
nativekeyword:
public class Example { public static void main(String[] args) { nativeAlert("Hello, world!"); }
public static native void nativeAlert(String message);}- Implement the native method in JavaScript, with an
asyncfunction namedJava_<fully-qualified-class-name>_<method-name>.nativeAlertis static, so the function has noselfparameter.
async function Java_Example_nativeAlert(lib, str) { window.alert(str);}The function Java_Example_nativeAlert implements the nativeAlert method of the Example class. It shows the message in an alert dialog with window.alert.
- Initialise CheerpJ with the
nativesoption, passing the native method implementation tocheerpjInit:
<!doctype html><html> <head> <title>Native Method Example</title> <script src="https://cjrtnc.leaningtech.com/5.0/loader.js"></script> </head> <body> <script type="module"> async function Java_Example_nativeAlert(lib, str) { window.alert(str); } // Init CheerpJ and register natives, then run your main await cheerpjInit({ natives: { Java_Example_nativeAlert }, }); await cheerpjRunMain("Example", "/app/"); </script> </body></html>cheerpjInit registers Java_Example_nativeAlert as the implementation of the native method. When Java calls Example.nativeAlert, CheerpJ runs the JavaScript Java_Example_nativeAlert function, which shows the message in an alert dialog.
Calling back into Java from JavaScript
A JavaScript native method can call back into Java through its lib parameter. The lib object gives access to your Java classes, so you can call their static methods, create objects and call their instance methods.
public class ClassName { // Implemented in JavaScript public static native void nativeMethodName();
public static void javaMethodName() { // Your Java logic here }
public static void main(String[] args) { nativeMethodName(); // Triggers the JS implementation }}The ClassName class declares a native method, nativeMethodName, implemented in JavaScript. It also has a public method, javaMethodName, with some Java logic.
The JavaScript implementation of nativeMethodName uses lib to load the ClassName class and call javaMethodName.
// Example placeholders — replace ClassName/javaMethodName with your ownasync function Java_ClassName_nativeMethodName(lib) { const ClassName = await lib.ClassName; // Access your Java class await ClassName.javaMethodName(); // Call a Java static method}This works for calls into Java made in response to a native method call. To call into Java at any other time, use a long-running Java thread, as shown in the Java and JavaScript Interoperability tutorial.
Converting parameters and return values
Parameters and return values of JNI calls are automatically converted between JavaScript and Java types based on conversion rules.