Преобразование jni :: sys :: JNIEnv в JNINativeInterface, определенный в ffi - PullRequest
1 голос
/ 15 февраля 2020

Я отслеживаю Преобразование заимствованной ссылки со временем жизни в необработанный указатель в 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 (жизненные проблемы, нулевые указатели и т. Д. c.).

Полный источник в определения ffi

Вот базовая c реализация 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 в принятом ответе.

1 Ответ

1 голос
/ 17 февраля 2020

A 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 и избегайте использования после освобождения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...