问题
I am following up on Casting a borrowed reference with a lifetime to a raw pointer in Rust, which solved the wrong problem.
Please consider the following code:
extern crate jni;
extern crate ffi;
use jni::JNIEnv;
use jni::objects::JClass;
use jni::sys::{jint, jlong, jobject};
struct CameraAppEngine {
_env: *mut jni::sys::JNIEnv,
_width: i32,
_height: i32
}
impl CameraAppEngine {
pub fn new(_env: *mut jni::sys::JNIEnv, _width: i32, _height: i32) -> CameraAppEngine {
CameraAppEngine { _env, _width, _height }
}
pub fn create_camera_session(&mut self, surface: jobject) {
// error!
let window = ffi::ANativeWindow_fromSurface(self._env, surface);
}
}
fn app_engine_create(env: &JNIEnv, width: i32, height: i32) -> *mut CameraAppEngine {
let engine = CameraAppEngine::new(env.get_native_interface(), width, height);
Box::into_raw(Box::new(engine))
}
#[no_mangle]
pub extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_createCamera(env: JNIEnv<'static>, _: JClass, width:jint, height:jint) -> jlong {
app_engine_create(&env, width, height) as jlong
}
#[no_mangle]
pub extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_onPreviewSurfaceCreated(_: JNIEnv, _: JClass, engine_ptr:jlong, surface:jobject) {
let mut app = unsafe { Box::from_raw(engine_ptr as *mut CameraAppEngine) };
app.create_camera_session(surface);
}
And in the ffi
crate we have:
extern "C" {
pub fn ANativeWindow_fromSurface(env: *mut JNIEnv, surface: jobject) -> *mut ANativeWindow;
}
This results in:
error[E0308]: mismatched types
--> native_app/src/lib.rs:24:53
|
| let window = ffi::ANativeWindow_fromSurface(self._env, surface);
| ^^^^^^^^^ expected struct `ffi::JNINativeInterface`, found struct `jni::sys::JNINativeInterface_`
|
= note: expected raw pointer `*mut *const ffi::JNINativeInterface`
found raw pointer `*mut *const jni::sys::JNINativeInterface_`
error[E0308]: mismatched types
--> native_app/src/lib.rs:24:64
|
| let window = ffi::ANativeWindow_fromSurface(self._env, surface);
| ^^^^^^^ expected enum `std::ffi::c_void`, found enum `jni::sys::_jobject`
|
= note: expected raw pointer `*mut std::ffi::c_void`
found raw pointer `*mut jni::sys::_jobject`
The problem is that the JNIEnv type expected by ANativeWindow_fromSurface
is actually unrelated to jni::sys::JNIEnv
entirely.
It's defined in ffi
like so:
pub type JNIEnv = *const JNINativeInterface;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct JNINativeInterface {
pub reserved0: *mut ::std::os::raw::c_void,
pub reserved1: *mut ::std::os::raw::c_void,
pub reserved2: *mut ::std::os::raw::c_void,
pub reserved3: *mut ::std::os::raw::c_void,
pub GetVersion: ::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv) -> jint>,
pub DefineClass: ::std::option::Option<
unsafe extern "C" fn(
arg1: *mut JNIEnv,
arg2: *const ::std::os::raw::c_char,
arg3: jobject,
arg4: *const jbyte,
arg5: jsize,
) -> jclass,
>,
pub FindClass: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: *const ::std::os::raw::c_char) -> jclass,
>,
pub FromReflectedMethod:
::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: jobject) -> jmethodID>,
pub FromReflectedField:
::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: jobject) -> jfieldID>,
pub ToReflectedMethod: ::std::option::Option<
unsafe extern "C" fn(
arg1: *mut JNIEnv,
arg2: jclass,
arg3: jmethodID,
arg4: jboolean,
) -> jobject,
>
// etc...
}
Given the glue code shown in the example, how do I get a valid reference to ffi::JNIEnv
so that I may pass it to the ANativeWindow_fromSurface
method. Bonus points if you give advice on converting jni::sys::jobject
to *mut std::os::raw::c_void
(lifetime concerns, null pointers, etc.).
Full source to ffi definitions
Here is the basic helloworld implementation I used to prove the concept in the accepted answer:
use std::ffi::{CString, CStr};
use std::os::raw::{c_char};
/// Expose the JNI interface for android below
#[cfg(target_os="android")]
#[allow(non_snake_case)]
pub mod android {
extern crate ffi;
use super::*;
use self::ffi::{JNIEnv, jclass, jstring, jlong};
#[derive(Debug)]
struct AppEngine {
greeting: *mut c_char
}
unsafe fn rust_greeting(app: *mut AppEngine) -> *mut c_char {
let app = Box::from_raw(app);
app.greeting
}
/// Constructs an AppEngine object.
fn rust_engine_create(to: *const c_char) -> *mut AppEngine {
let c_str = unsafe { CStr::from_ptr(to) };
let recipient = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let greeting = CString::new("Hello ".to_owned() + recipient).unwrap().into_raw();
let app = AppEngine{greeting: greeting};
Box::into_raw(Box::new(app))
}
/// Destroys an AppEngine object previously constructed using `rust_engine_create()`.
unsafe fn rust_engine_destroy(app: *mut AppEngine) {
drop(Box::from_raw(app))
}
#[no_mangle]
pub unsafe extern fn Java_io_waweb_cartoonifyit_MainActivity_greeting(env: &mut JNIEnv, _: jclass, app_ptr: jlong) -> jstring {
let app = app_ptr as *mut AppEngine;
let new_string = env.as_ref().unwrap().NewStringUTF.unwrap();
new_string(env, rust_greeting(app))
}
#[no_mangle]
pub unsafe extern fn Java_io_waweb_cartoonifyit_MainActivity_createNativeApp(env: &mut JNIEnv, _: jclass, java_pattern: jstring) -> jlong {
let get_string_chars = env.as_ref().unwrap().GetStringChars.unwrap();
let is_copy = 0 as *mut u8;
rust_engine_create(get_string_chars(env, java_pattern, is_copy) as *const c_char ) as jlong
}
#[no_mangle]
pub unsafe extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_destroyNativeApp(_: JNIEnv, _: jclass, app_ptr: jlong) {
let app = app_ptr as *mut AppEngine;
rust_engine_destroy(app)
}
}
This is just a proof of concept. More care should be taken when casting raw pointers about. Also see notes about Box::leak
in the accepted answer.
回答1:
A JNIEnv
is a pointer to a struct used for communication between Java and native code. This communication ABI is implemented by pretty much every JVM(and android). There are multiple versions of the aforementioned structs, which is what the GetVersion
field is for.
It seems to me that you are using an external jni
crate along with your own ffi
crate generated from this wrapper. I would expect your ffi
crate to be the most correct since it is using android headers, instead of the standard JVM headers which a most likely being used by the jni
crate.
One last note Box::from_raw(engine_ptr as *mut CameraAppEngine)
, creates a box which will free the memory located at engine_ptr
. This is likely not what you want. Consider using Box::leak
to leak the created Box
and avoid use after frees.
来源:https://stackoverflow.com/questions/60242423/converting-jnisysjnienv-to-jninativeinterface-defined-in-ffi