Interoperability with Java

Java can load shared objects via Java Native Interface (JNI). The jni crate allows you to create a compatible library.

First, we create a Rust function to export to Java:

interoperability/java/src/lib.rs:

1. ```
   #![allow(unused)]
   ```
2. ```
   fn main() {
   ```
3. ```
   //! Rust <-> Java FFI demo.
   ```

5. ```
   use jni::objects::{JClass, JString};
   ```
6. ```
   use jni::sys::jstring;
   ```
7. ```
   use jni::JNIEnv;
   ```

9. ```
   /// HelloWorld::hello method implementation.
   ```
10. ```
    // SAFETY: There is no other global function of this name.
    ```
11. ```
    #[unsafe(no_mangle)]
    ```
12. ```
    pub extern "system" fn Java_HelloWorld_hello(
    ```
13. ```
        mut env: JNIEnv,
    ```
14. ```
        _class: JClass,
    ```
15. ```
        name: JString,
    ```
16. ```
    ) -> jstring {
    ```
17. ```
        let input: String = env.get_string(&name).unwrap().into();
    ```
18. ```
        let greeting = format!("Hello, {input}!");
    ```
19. ```
        let output = env.new_string(greeting).unwrap();
    ```
20. ```
        output.into_raw()
    ```
21. ```
    }
    ```
22. ```
    }
    ```

interoperability/java/Android.bp:

1. rust_ffi_shared {
2. name: "libhello_jni",
3. crate_name: "hello_jni",
4. srcs: ["src/lib.rs"],
5. rustlibs: ["libjni"],
6. }

We then call this function from Java:

interoperability/java/HelloWorld.java:

1. class HelloWorld {
2. private static native String hello(String name);

4. static {
5. System.loadLibrary("hello_jni");
6. }

8. public static void main(String[] args) {
9. String output = HelloWorld.hello("Alice");
10. System.out.println(output);
11. }
12. }

interoperability/java/Android.bp:

1. java_binary {
2. name: "helloworld_jni",
3. srcs: ["HelloWorld.java"],
4. main_class: "HelloWorld",
5. required: ["libhello_jni"],
6. }

Finally, you can build, sync, and run the binary:

1. m helloworld_jni
2. adb sync  # requires adb root && adb remount
3. adb shell /system/bin/helloworld_jni