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

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);
  3. static {
  4. System.loadLibrary("hello_jni");
  5. }
  6. public static void main(String[] args) {
  7. String output = HelloWorld.hello("Alice");
  8. System.out.println(output);
  9. }
  10. }

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