Skip to content
This repository has been archived by the owner on Jul 3, 2020. It is now read-only.

Initial JNI support #116

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Initial JNI support #116

wants to merge 2 commits into from

Conversation

wvdschel
Copy link
Contributor

@wvdschel wvdschel commented Nov 2, 2016

Feedback welcome!

@wvdschel
Copy link
Contributor Author

wvdschel commented Nov 2, 2016

Example code using these JNI bindings can be seen in rust-windowing/glutin#822

//}

#[repr(C)]
pub struct JNINativeInterface {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was simply copied from injected-glue/ffi.rs

static JNI_HANDLE : RefCell<JNIEnvHandle> = RefCell::new(JNIEnvHandle { jni_env: ptr::null_mut(), counter: 0 });
}

pub fn attach_thread() -> JNIEnv {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detaching a thread in JNI is immediate, so nested attach/detach pairs are a pain. I've added simple reference counting here, so every call can simply enclose their code in jni::attach_thread()/jni::detach_thread() pairs, and only the final call will actually detach the thread.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're providing a high-level interface, I think attach_thread should return an RAII guard that automatically calls detach_thread in its Drop implementation.

/// This static variable will store the android_app* on creation, and set it back to 0 at
/// destruction.
/// Apart from this, the static is never written, so there is no risk of race condition.
#[no_mangle]
pub static mut ANDROID_APP: *mut ffi::android_app = 0 as *mut ffi::android_app;
pub static mut CLASS_LOADER: ffi::jobject = 0 as ffi::jobject;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLASS_LOADER and ACTIVITY need some documentation for what they are.

static JNI_HANDLE : RefCell<JNIEnvHandle> = RefCell::new(JNIEnvHandle { jni_env: ptr::null_mut(), counter: 0 });
}

pub fn attach_thread() -> JNIEnv {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're providing a high-level interface, I think attach_thread should return an RAII guard that automatically calls detach_thread in its Drop implementation.

@tomaka
Copy link
Contributor

tomaka commented Nov 2, 2016

Overall I think this needs some more high-level functions as well. But I'm not sure what the right border should be between the low-level bindings and a high-level wrapper, so this could eventually be done in a later PR.

@tomaka
Copy link
Contributor

tomaka commented Nov 2, 2016

Here's a gist @sfackler made demonstrating a high-level API for JNI stuff: https://gist.github.com/sfackler/5c036006f8d58a1d38887ba014759f74
It is licensed under MIT/Apache2 so we can copy some stuff if we want.

As I said in the previous comment, this might be out of scope of this PR but I thought I'd leave it somewhere for reference.

@sfackler
Copy link

sfackler commented Nov 2, 2016

Might also be worth depending on https://crates.io/crates/jni-sys to avoid needing to have a second copy in here. Happy to make any changes necessary on that end.

@wvdschel
Copy link
Contributor Author

wvdschel commented Nov 3, 2016

@tomaka, what is your view on using @sfackler's jni-sys crate?
I'd prefer not to re-implement anything that we can readily reuse, but I'm not sure if you're happy to be adding another dependency.

Before I start addressing other feedback, it would be nice to decide if using jni-sys is on the table, and if we should add higher level wrappers to android-rs-glue, jni-sys, or some intermediate crate that wraps raw jni-sys in a nice, safe, Rust-y API.

I agree that adding a higher level API (like JNIEnv's that automatically detach_thread(), or jobject's that automatically DeleteGlobalRef/DeleteLocalRef when dropped). Most of all, I'd like to get rid of the various explicit method calls based on types.

But I'm also not sure this should be part of android-rs-glue.

@sfackler I don't think any explicit changes are required from looking at your code for a brief moment. There are some differences in the JNI implementation of Android, but those mostly (as far as I've read - I'm no expert) relate to how the JNI API is used, not the API itself.

@wvdschel
Copy link
Contributor Author

wvdschel commented Nov 3, 2016

An alternative approach would be to remove the JNI API from the glue module, and simply using the four methods provided by this patch in injected-glue from jni-sys, guarded by some #![cfg(target_os = "android")].

This would mean that android-glue-rs would not provide JNI itself, but JNI support can optionally be obtained when also including the jni-sys crate.

@tomaka
Copy link
Contributor

tomaka commented Nov 3, 2016

In my opinion this is the ideal design:

  • The internal and the external glues both depend on jni-sys and communicate with each other to share the low-level objects (like you have done in your PR).
  • The external glue also depends on a new crate (named jni for example) that doesn't exist yet and that provides a high-level wrapper around the JNI.
  • The high-level wrappers are reexported in the external glue's API, but the low-level objects are not exposed.

@wvdschel
Copy link
Contributor Author

wvdschel commented Nov 3, 2016

I can agree with that. I'll look into making the adaptations over the weekend, probably.

The higher level JNI wrapper will probably take a longer time to complete.

@sfackler Are you interested in creating a repo for the higher level wrapper, or should I?

@sfackler
Copy link

sfackler commented Nov 3, 2016

I probably don't have the time to build out the high level wrapper right now unfortunately, so you might want to get it started.

@wvdschel
Copy link
Contributor Author

Just a note that I have started on reworking this, but haven't found the time to finish it yet (because life).

@fschutt
Copy link

fschutt commented Apr 1, 2018

So ... what is the current state? The jni_sys crate is pretty mature now and it's been two years ...

@fschutt
Copy link

fschutt commented Apr 1, 2018

Why can't the ffi module simply be exposed / exported to the library user? This way the user can hook up the jni crate and from there on it's rather easy to interact with the JNI. I know about "API concerns", but .. something is better than nothing.

@tomaka
Copy link
Contributor

tomaka commented Apr 1, 2018

I'm ok with exposing the JNI-related functions of the FFI, as long as they are contained within their own scope and don't require an external crate.

@fschutt
Copy link

fschutt commented Apr 2, 2018

@tomaka ... why? What's wrong about the jni crate? I mean ... do you want to re-do all of the work they've done just to not depend on them - how would you then do this function, if not using jni?

@tomaka
Copy link
Contributor

tomaka commented Apr 3, 2018

Sorry, what I mean is that we should simply reexport the content of the jni-sys crate from our own crate, instead of exposing it in the API.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants