将 jni::sys::JNIEnv 转换为 ffi 中定义的 JNINativeInterface

Converting jni::sys::JNIEnv to JNINativeInterface defined in ffi

我正在跟进,解决了错误的问题。

请考虑以下代码:

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);
}

ffi 板条箱中我们有:

extern "C" {
    pub fn ANativeWindow_fromSurface(env: *mut JNIEnv, surface: jobject) -> *mut ANativeWindow;
}

这导致:

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`

问题是 ANativeWindow_fromSurface 期望的 JNIEnv 类型实际上与 jni::sys::JNIEnv 完全无关。

它在 ffi 中定义如下:

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...
}

鉴于示例中显示的粘合代码,我如何获得对 ffi::JNIEnv 的有效引用,以便我可以将其传递给 ANativeWindow_fromSurface 方法。如果您提供有关将 jni::sys::jobject 转换为 *mut std::os::raw::c_void 的建议(生命周期问题、空指针等),则可加分。

Full source to ffi definitions

这是我用来证明已接受答案中的概念的基本 helloworld 实现:

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)
    }
}

这只是概念验证。转换原始指针时应更加小心。另请参阅接受的答案中有关 Box::leak 的注释。

A JNIEnv 是指向用于 Java 和本机代码之间通信的结构的指针。几乎每个 JVM(和 android)都实现了这种通信 ABI。上述结构有多个版本,这就是 GetVersion 字段的用途。

在我看来,您正在使用外部 jni crate 以及由此 wrapper 生成的您自己的 ffi crate。我希望你的 ffi crate 是最正确的,因为它使用的是 android headers,而不是标准的 JVM headers,后者最有可能被 jni箱子。

最后一个音符 Box::from_raw(engine_ptr as *mut CameraAppEngine),创建一个框,将释放位于 engine_ptr 的内存。这可能不是您想要的。考虑使用 Box::leak 泄漏创建的 Box 并避免在释放后使用。