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:

#![allow(unused)] fn main() { //! Rust <-> Java FFI demo. use jni::objects::{JClass, JString}; use jni::sys::jstring; use jni::JNIEnv; /// HelloWorld::hello method implementation. // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] pub extern "system" fn Java_HelloWorld_hello( mut env: JNIEnv, _class: JClass, name: JString, ) -> jstring { let input: String = env.get_string(&name).unwrap().into(); let greeting = format!("Hello, {input}!"); let output = env.new_string(greeting).unwrap(); output.into_raw() } }

interoperability/java/Android.bp:

rust_ffi_shared { name: "libhello_jni", crate_name: "hello_jni", srcs: ["src/lib.rs"], rustlibs: ["libjni"], }

We then call this function from Java:

interoperability/java/HelloWorld.java:

class HelloWorld { private static native String hello(String name); static { System.loadLibrary("hello_jni"); } public static void main(String[] args) { String output = HelloWorld.hello("Alice"); System.out.println(output); } }

interoperability/java/Android.bp:

java_binary { name: "helloworld_jni", srcs: ["HelloWorld.java"], main_class: "HelloWorld", required: ["libhello_jni"], }

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

m helloworld_jni adb sync # requires adb root && adb remount adb shell /system/bin/helloworld_jni

Speaker Notes

  • The unsafe(no_mangle) attribute instructs Rust to emit the Java_HelloWorld_hello symbol exactly as written. This is important so that Java can recognize the symbol as a hello method on the HelloWorld class.

    • By default, Rust will mangle (rename) symbols so that a binary can link in two versions of the same Rust crate.