最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - Returning multiple values (Pair) from Rust to Android through JNI - Stack Overflow

programmeradmin0浏览0评论

I have an Android app (written in Kotlin), with the processing code running in Rust. I'm using JNI (Java Native Interface) as communication layer.

Goal

I can successfully return a bool, but I want to extend the Rust function so it returns both a bool and string.

Working code (returns Boolean only)

Android (Kotlin)

On the Kotlin side I have the following function signature:

package eu.mypackage.rust

class SomeName {
    @Throws(IllegalArgumentException::class)
    external fun checkAnswer(answerInput: String, answerList: Array<String>): Boolean
}

Which I call with:

fun processAnswer(answerInput: String, answerList: List<String>): Int {
    val isCorrect = sn.checkAnswer(answerInput, answerList.toTypedArray())
    ...

Rust

On the Rust side I have the followingcode that is successfully :

#[cfg(target_os = "android")]
#[allow(non_snake_case)]
pub mod android {
    extern crate jni;

    // This is the interface to the JVM  that we'll call the majority of our methods on.
    // @See /
    use self::jni::JNIEnv;
    use self::jni::objects::{JClass, JString, JObjectArray};
    use self::jni::sys::{jstring, jboolean, jint};

    #[no_mangle] // This keeps Rust from "mangling" the name so it is unique (crate).
    pub extern "system" fn Java_eu_mycompany_rust_MyPackage_checkAnswer<'local>(
        mut env: JNIEnv<'local>,
         _class: JClass<'local>,
        answerInput: JString<'local>,
        answerList: JObjectArray<'local>,
    ) -> jboolean
    // Keep in mind that arrays of jboolean values should only ever hold values of 0 or 1 because any other value could lead to undefined behaviour within the JVM.
    // .JNIEnv.html#jboolean-elements
    {
        let answer_input: String = env.get_string(&answerInput).expect("Couldn't get java string!").into();
        let answer_list = kotlin_list_to_rust_vec_string(&mut env, &answerList);

        // pub fn answer_in_answer_list<'a>(answer_input: &String, answer_list: &'a Vec<String>) -> (bool, Option<&'a String>)
        let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list);

        // TODO return both `is_correct` as bool and `corrected_answer` as string
        return if is_correct {
            1  // true
        } else {
            0  // false
        }
    }
}

Attempt

Kotlin

I changed the expected output type to: Pair<Boolean, String>

package eu.mypackage.rust

class SomeName {
    @Throws(IllegalArgumentException::class)
    external fun checkAnswer(answerInput: String, answerList: Array<String>): Pair<Boolean, String>
}

and the update calling code in Kotlin:

fun processAnswer(answerInput: String, answerList: List<String>): Int {
    val (isCorrect, corrected_answer) = sn.checkAnswer(answerInput, answerList.toTypedArray())
    ...

Rust

However, on the Rust side I'm lost. I tried returning a tuple as jobject with the following code, but it crashes my Android app with a panic_abort and (core::result::unwrap_failed::h5cd03753bd6180f5+88):

        let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list);

        // Create a Java boolean (1 for true, 0 for false)
        let j_is_correct: jboolean = if is_correct { 1 } else { 0 };

        // Convert the Rust string to a JNI string if exist, else empty JNI string
        let j_corrected_answer = match corrected_answer {
            Some(s) => env.new_string(&s).expect("Couldn't create Java string!"),
            // empty String if no correction
            None => env.new_string("").expect("Couldn't create Java string!")
        };

        // Create a Java Pair
        let result_tuple = env
            .new_object(
                "android/util/Pair",
                "(Ljava/lang/Object;Ljava/lang/Object;)V",
                &[j_is_correct.into(), (&j_corrected_answer).into()]
            )
            .expect("Couldn't create Pair object");
        
        result_tuple.into_raw()

I have an Android app (written in Kotlin), with the processing code running in Rust. I'm using JNI (Java Native Interface) as communication layer.

Goal

I can successfully return a bool, but I want to extend the Rust function so it returns both a bool and string.

Working code (returns Boolean only)

Android (Kotlin)

On the Kotlin side I have the following function signature:

package eu.mypackage.rust

class SomeName {
    @Throws(IllegalArgumentException::class)
    external fun checkAnswer(answerInput: String, answerList: Array<String>): Boolean
}

Which I call with:

fun processAnswer(answerInput: String, answerList: List<String>): Int {
    val isCorrect = sn.checkAnswer(answerInput, answerList.toTypedArray())
    ...

Rust

On the Rust side I have the followingcode that is successfully :

#[cfg(target_os = "android")]
#[allow(non_snake_case)]
pub mod android {
    extern crate jni;

    // This is the interface to the JVM  that we'll call the majority of our methods on.
    // @See https://docs.rs/jni/latest/jni/
    use self::jni::JNIEnv;
    use self::jni::objects::{JClass, JString, JObjectArray};
    use self::jni::sys::{jstring, jboolean, jint};

    #[no_mangle] // This keeps Rust from "mangling" the name so it is unique (crate).
    pub extern "system" fn Java_eu_mycompany_rust_MyPackage_checkAnswer<'local>(
        mut env: JNIEnv<'local>,
         _class: JClass<'local>,
        answerInput: JString<'local>,
        answerList: JObjectArray<'local>,
    ) -> jboolean
    // Keep in mind that arrays of jboolean values should only ever hold values of 0 or 1 because any other value could lead to undefined behaviour within the JVM.
    // https://docs.rs/jni/latest/jni/struct.JNIEnv.html#jboolean-elements
    {
        let answer_input: String = env.get_string(&answerInput).expect("Couldn't get java string!").into();
        let answer_list = kotlin_list_to_rust_vec_string(&mut env, &answerList);

        // pub fn answer_in_answer_list<'a>(answer_input: &String, answer_list: &'a Vec<String>) -> (bool, Option<&'a String>)
        let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list);

        // TODO return both `is_correct` as bool and `corrected_answer` as string
        return if is_correct {
            1  // true
        } else {
            0  // false
        }
    }
}

Attempt

Kotlin

I changed the expected output type to: Pair<Boolean, String>

package eu.mypackage.rust

class SomeName {
    @Throws(IllegalArgumentException::class)
    external fun checkAnswer(answerInput: String, answerList: Array<String>): Pair<Boolean, String>
}

and the update calling code in Kotlin:

fun processAnswer(answerInput: String, answerList: List<String>): Int {
    val (isCorrect, corrected_answer) = sn.checkAnswer(answerInput, answerList.toTypedArray())
    ...

Rust

However, on the Rust side I'm lost. I tried returning a tuple as jobject with the following code, but it crashes my Android app with a panic_abort and (core::result::unwrap_failed::h5cd03753bd6180f5+88):

        let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list);

        // Create a Java boolean (1 for true, 0 for false)
        let j_is_correct: jboolean = if is_correct { 1 } else { 0 };

        // Convert the Rust string to a JNI string if exist, else empty JNI string
        let j_corrected_answer = match corrected_answer {
            Some(s) => env.new_string(&s).expect("Couldn't create Java string!"),
            // empty String if no correction
            None => env.new_string("").expect("Couldn't create Java string!")
        };

        // Create a Java Pair
        let result_tuple = env
            .new_object(
                "android/util/Pair",
                "(Ljava/lang/Object;Ljava/lang/Object;)V",
                &[j_is_correct.into(), (&j_corrected_answer).into()]
            )
            .expect("Couldn't create Pair object");
        
        result_tuple.into_raw()
Share Improve this question asked Mar 13 at 7:17 NumesSanguisNumesSanguis 6,4028 gold badges46 silver badges81 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

jboolean is the JNI version of the Java primitive boolean. Thus it is not a heir of java.lang.Object.

The constructor of android.util.Pair requires two object instances, which means jboolean is not a compatible type.

On Java level modern compiler implements "autoboxing", which automatically converts between primitives and objects (like boolean to/from Boolean) but on JNI level you have to manually do this.

发布评论

评论列表(0)

  1. 暂无评论