Преобразование jni :: sys :: JNIEnv в JNINativeInterface, определенный в ffi

Я слежу за кастингом заимствованная ссылка со временем жизни на необработанный указатель в Rust, которая решила неправильную проблему.

Обратите внимание на следующий код:

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`

Проблема в том, что тип JNIEnv, ожидаемый ANativeWindow_fromSurface, на самом деле не связан с 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 (проблемы со сроком службы, нулевые указатели и т. Д.).

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 в принятом ответе.


person drkstr101    schedule 15.02.2020    source источник
comment
Правильно ли я догадываюсь, что это на android?   -  person PiRocks    schedule 16.02.2020
comment
По сути, ffi::JNINativeInterface и jni::sys::JNINativeInterface_ - это одна и та же структура, и поэтому вы должны иметь возможность выполнять приведение между ними, не вызывая ошибок сегментации. Однако мне любопытно, почему вы вообще используете ящик jni? Получается, что в ffi есть все нужные вам типы? Наконец, ящик jni не предназначен для использования с android afaik.   -  person PiRocks    schedule 16.02.2020
comment
Возможно, мне действительно не нужен jni ящик. Это то, что использовалось во всех примерах, показывающих взаимодействие android / rust. EG Java_io_waweb_cartoonifyit_MainActivity_createCamera автоматически получает JNIEnv. Возможно, я попробую изменить этот тип на ffi :: JniEnv и посмотреть, что будет. Спасибо за совет!   -  person drkstr101    schedule 16.02.2020
comment
@PiRocks Да, это приложение для Android. В частности, я пытаюсь перенести просмотр текстуры Пример приложения NDKCamera.   -  person drkstr101    schedule 16.02.2020


Ответы (1)


JNIEnv - это указатель на структуру, используемую для связи между Java и собственным кодом. Этот коммуникационный ABI реализуется практически каждой JVM (и Android). Существует несколько версий вышеупомянутых структур, для чего и предназначено поле GetVersion.

Мне кажется, что вы используете внешний jni ящик вместе со своим собственным ffi ящиком, созданным из этого оболочка. Я ожидаю, что ваш ящик ffi будет наиболее правильным, поскольку он использует заголовки android вместо стандартных заголовков JVM, которые, скорее всего, используются ящиком jni.

Последнее примечание Box::from_raw(engine_ptr as *mut CameraAppEngine), создает поле, которое освобождает память, расположенную в engine_ptr. Скорее всего, это не то, что вам нужно. Рассмотрите возможность использования Box::leak для утечки созданного Box и избегайте использования после освобождения.

person PiRocks    schedule 16.02.2020
comment
здесь, где я застрял. Перед фиксацией приветствия приложение world запускается должным образом (с ящиком jni). Использование символов из ffi приводит к жалобе на то, что GetStringChars - это поле, а не метод. Я даже не совсем уверен, как вызывать эти методы, не говоря уже о том, что происходит во время выполнения, когда я вставляю свой собственный JNIEnv тип в сигнатуру метода. - person drkstr101; 17.02.2020
comment
Я немного ближе познакомился с этим, так что теперь мне просто нужно разобраться как ожидаемый необработанный указатель *const u8, найден необработанный указатель *const u16. По крайней мере, я подхожу ближе. Я отмечу как принятый, как только смогу использовать ffi:JNIEnv в моем примере helloworld. - person drkstr101; 17.02.2020
comment
Победа!. Немного сложно работать со всеми необработанными типами, но ffi::JNIEnv полностью заменяет jni::JNIEnv. Большое вам спасибо, сэр! - person drkstr101; 17.02.2020
comment
Я полагаю, вы уже это поняли, но GetStringChars - это указатель на функцию в поле. См. doc.rust-lang.org/reference/types/function-pointer .html для получения дополнительной информации. - person PiRocks; 17.02.2020
comment
Ага, мне не хватало лишнего unwrap(). Также первый параметр не был &self, вместо этого мне пришлось передать &mut JNIEnv. Я опубликовал полное доказательство концепции выше, если вам интересно. Спасибо еще раз за помощь! - person drkstr101; 17.02.2020