Асинхронный обратный вызов Rust JNI с Tokio и Reqwest для Android

Я тестирую Rust с асинхронным выполнением JNI. Я хочу выполнять запросы в Rust и возвращать результат в Android асинхронно с обратным вызовом. Я тестирую код для выполнения запроса в командной строке, и он отлично работает.

Вот как это работает в командной строке:

Обратный вызов отправлен:

struct Processor {
    pub(crate) callback: Box<dyn FnMut(String)>,
}

impl Processor {

    fn set_callback(&mut self, c: impl FnMut(String) + 'static) {
        self.callback = Box::new(c);
    }

    fn process_events(&mut self, result: String) {
        (self.callback)(result);
    }
}

Tokio / reqwest:

const DATA_URL: &str = "https://pokeapi.co/api/v2/pokemon/1/";

#[tokio::main]
pub async fn load_swapi_async_with_cb(callback: Box<dyn FnMut(String)>) -> Result<(), Box<dyn std::error::Error>> {
    println!("load_swload_swapi_async_with_cbapi_async started");
    let mut cb = Processor {
        callback: Box::new(callback),
    };
    let body = reqwest::get(DATA_URL)
        .await?
        .json::<HashMap<String, String>>()
        .await?;
    //println!("{:#?}", body);
    let name = match body.get("name") {
        Some(name) => name,
        None => "Failed to parse"
    }.to_string();

    println!("Name is: {} ", name);
    cb.process_events(name);
    Ok(())
}

И часть JNI:

    #[no_mangle]
    #[allow(non_snake_case)]
    pub extern "C" fn Java_com_omg_app_greetings_MainActivity_callback(env: JNIEnv,
                                                                       _class: JClass,
                                                                       callback: JObject) {

        static callback: dyn FnMut(String) + 'static = |name| {
        let response = env.new_string(&name).expect("Couldn't create java string!");
            env.call_method(callback, "rustCallbackResult", "(Ljava/lang/String;)V",
                        &[JValue::from(JObject::from(response))]).unwrap();
        };

        pokemon_api(callback);
    }

И метод Pokemon API:

#[no_mangle]
pub extern fn pokemon_api(callback: impl FnMut(String) + 'static) {
    let cb_box = Box::new(callback);
    swapi::load_swapi_async_with_cb(cb_box);
}

Я столкнулся с ошибкой:

  • JNI ENV env non-constant value:
let response = env.new_string(&name).expect("Couldn't create java string!");
   |                        ^^^ non-constant value

  • обратный звонок - doesn't have a size known at compile-time:
static callback: dyn FnMut(String) + 'static = |name| {
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time

Я проверял, как это работает, но пример, похоже, устарел: * https://github.com/mozilla/rust-android-gradle/blob/master/samples/rust/src/lib.rs


person CVS    schedule 15.04.2020    source источник
comment
users.rust-lang.org/t/rust- jni-android-async-callback / 41016   -  person CVS    schedule 15.04.2020
comment
Я мало что знаю о Rust, но будет ли обратный вызов выполняться в другом потоке? Каждый JNIEnv* привязан к потоку, который его создал, и использование JNIEnvs в другом потоке является фатальной ошибкой. К счастью, вы можете AttachCurrentThreadAsDaemon чтобы получить действительный JNIEnv.   -  person Botje    schedule 16.04.2020
comment
Я никогда не думал об этом ... позвольте мне исследовать это подробнее. Спасибо   -  person CVS    schedule 17.04.2020


Ответы (1)


Я исправил свою проблему с использованием https://github.com/Dushistov/rust_swig после вы интегрируете его, он автоматически сгенерирует код, и вы можете проверить, как он это делает.

person CVS    schedule 03.05.2020