Code Monkey home page Code Monkey logo

Comments (2)

lexi-the-cute avatar lexi-the-cute commented on June 16, 2024

I should mention, I have absolutely no idea what I'm doing when it comes to JNI. While I have managed to successfully create Toasts and Notifications on Android from rust via JNI, my code is probably not the most legible. If there's a cleaner solution, please let me know so I can improve the quality of my code

from jni-rs.

argv-minus-one avatar argv-minus-one commented on June 16, 2024

I'd preferably like to not have to implement a custom class for these function, however if it's the only way, I'd like help understanding how to do it.

I'm afraid that's what you have to do, yes.

If, for example, you need to implement java.util.function.Function<T, R>, you'll need to implement it along these lines:

package com.example;

public final class ExampleCallback implements java.util.function.Function<String, String> {
	// Pointer to native memory storing the callback function.
	private long ptr;

	// Constructor. Called by native code.
	private ExampleCallback() {}

	// Deallocates native memory. Called by the garbage collector.
	@Override
	protected native void finalize() throws Throwable;

	// Implements `java.util.function.Function`.
	@Override
	public native String apply(String t);
}

…and then implement apply and finalize in Rust, and fill in the ptr from Rust:

use jni::{
	JNIEnv,
	objects::{JClass, JObject, JString},
};
use std::{
	borrow::Cow,
	panic::{AssertUnwindSafe, catch_unwind},
	sync::MutexGuard,
};

// The callback function, in a box.
type BoxedCallback = Box<dyn Fn(&str) -> String + Send + 'static>;

// Instantiates a new `ExampleCallback` object, which will use `f` as the native callback.
fn instantiate_java_callback<'local>(
	env: &mut JNIEnv<'local>,
	f: impl Fn(&str) -> String + Send + Sync + 'static,
) -> jni::errors::Result<JObject<'local>> {
	// Box the callback function so we can store it in a Java object.
	let f: BoxedCallback = Box::new(f);

	// Instantiate the Java object.
	let object: JObject<'local> = env.new_object(
		"com/example/ExampleCallback",
		"()V",
		&[],
	)?;

	// Give ownership of the callback function to the Java object, by storing the pointer in its
	// `ptr` field.
	unsafe { env.set_rust_field(&object, "ptr", f)?; }

	// We're done here. Return the Java object.
	Ok(object)
}

// Implementation of `ExampleCallback.apply`. Extracts the callback function pointer and calls it.
#[no_mangle]
extern "system" fn Java_com_example_ExampleCallback_apply<'local>(
	env: JNIEnv<'local>,
	this: JObject<'local>,
	string: JString<'local>,
) -> JString<'local> {
	quick_and_dirty_error_handler(env, |mut env| {
		// Convert the input string from Java to Rust.
		let string = env.get_string(&string)?;
		let string: Cow<str> = (&string).into();

		// Get the pointer to the callback function.
		let f: MutexGuard<BoxedCallback> = unsafe {
			// Safety: The `ptr` field is only ever written by `JNIEnv::set_rust_field`, using the same
			// value type (`BoxedCallback`).
			env.get_rust_field(&this, "ptr")
		}?;

		// Call the callback function.
		let string: String = f(&*string);

		// Drop the mutex lock so we can use the `JNIEnv` again.
		drop(f);

		// Convert the output string back to Java, and return it.
		env.new_string(string)
	})
}

// Implementation of `ExampleCallback.finalize`. Deallocates the callback function.
#[no_mangle]
extern "system" fn Java_com_example_ExampleCallback_finalize(
	mut env: JNIEnv,
	this: JObject,
) {
	// Retake ownership of the box from Java.
	let _: jni::errors::Result<BoxedCallback> = unsafe {
		// Safety: The `ptr` field is only ever written by `JNIEnv::set_rust_field`, using the same
		// value type (`BoxedCallback`).
		env.take_rust_field(&this, "ptr")
	};

	// At this point, the `Box` (if any) will deallocate, freeing the memory it held, including the
	// inner `Box` and the callback function itself.
}

// Quick and dirty error handler. Handles errors and panics in a JNI-friendly way.
//
// This will hopefully be unnecessary in a future version of the jni crate.
// See discussion at: https://github.com/jni-rs/jni-rs/issues/432
fn quick_and_dirty_error_handler<'local, T>(
	env: JNIEnv<'local>,
	f: impl FnOnce(JNIEnv<'local>) -> jni::errors::Result<T>,
) -> T
where
	T: Default,
{
	let mut env_clone = unsafe { env.unsafe_clone() };

	match catch_unwind(AssertUnwindSafe(|| f(env))) {
		Ok(Ok(ok)) => ok,

		Err(_) => {
			if !(matches!(env_clone.exception_check(), Ok(true))) {
				let _ = env_clone.throw_new("java/lang/Error", "there was a Rust panic");
			}

			Default::default()
		}

		Ok(Err(error)) => {
			if !(matches!(env_clone.exception_check(), Ok(true))) {
				let _ = env_clone.throw_new("java/lang/Error", format!("there was an error: {error}"));
			}

			Default::default()
		}
	}
}

Now you can instantiate those callback objects from Rust code, like this:

instantiate_java_callback(&mut env, |s| format!("{s}!"))

Note that this uses JNIEnv::{set,take}_rust_field, which has some issues, most notably that the Java method finalize is deprecated. See #535 for discussion on this issue.

from jni-rs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.