Comments (2)
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.
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)
- pass rust function as implementation for Java object HOT 5
- JavaVM should be Clone HOT 1
- `get_static_field_unchecked` was not updated to use ReturnType in #344
- `{get,set}[_static]_field_unchecked` should be `unsafe`
- How to add elements into an `JObjectArray`? HOT 2
- `JNIStr` should implement `to_str()` and not `Deref` directly to a `CStr`
- How to pass JavaVM from c to rust HOT 2
- `JClass::from`, `JString::from`, `JThrowable::from`, etc. safety HOT 2
- Byte type confusion
- Feature request: JString from JChar slice via NewString
- How to return jobject as kotlin data class
- New release on crates.io
- Could not compile jni (build script) HOT 1
- JNIEnv::register_native_methods should be marked as unsafe
- JNIEnv::get_string should delete the local ref of string_class HOT 1
- Performance enhancement in get_string
- `new_object_unchecked` should accept`Desc<'local, JMethodID>`
- `JNIEnv::take_rust_field` seems meant for use with `java.lang.Object.finalize`, but that is deprecated
- Unsound usages of unsafe implementation from small size to large size HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from jni-rs.