如何通过JNI从Rust向Android返回布尔值与字符串Pair
JNI实现Rust向Android返回Pair<Boolean, String>的解决方案
问题背景
我开发了一个基于Kotlin的Android应用,核心处理逻辑由Rust编写,通过JNI通信。原本已实现Rust向Android返回单个bool值,现在需要扩展为同时返回bool和string类型数据,将Kotlin端函数返回类型改为Pair<Boolean, String>后,应用崩溃,报错panic_abort和(core::result::unwrap_failed::h5cd03753bd6180f5+88)。
崩溃原因分析
- 返回类型不匹配:Rust端函数签名仍保留原返回类型
jboolean,但实际需要返回jobject类型的Pair对象,类型不匹配触发错误。 - 基本类型直接转Object错误:Pair构造函数接收的是
Object类型参数,直接将jboolean(基本类型)转为jobject传入,会导致JVM类型校验失败,触发panic。
正确实现步骤
1. Android(Kotlin)端代码(保持现有修改)
Kotlin端的函数声明和调用逻辑无需调整,已匹配需求:
package eu.mypackage.rust class SomeName { @Throws(IllegalArgumentException::class) external fun checkAnswer(answerInput: String, answerList: Array<String>): Pair<Boolean, String> }
调用代码:
fun processAnswer(answerInput: String, answerList: List<String>): Int { val (isCorrect, corrected_answer) = sn.checkAnswer(answerInput, answerList.toTypedArray()) // 后续业务逻辑 }
2. Rust端代码修正
2.1 修改函数签名返回类型
将JNI函数的返回类型从jboolean改为jobject,匹配Pair对象类型:
pub extern "system" fn Java_eu_mycompany_rust_MyPackage_checkAnswer<'local>( mut env: JNIEnv<'local>, _class: JClass<'local>, answerInput: JString<'local>, answerList: JObjectArray<'local>, ) -> jobject // 替换原jboolean
2.2 完整Rust实现代码
核心修正点:将基本类型jboolean通过Boolean.valueOf()转为包装类对象,再传入Pair构造函数:
#[cfg(target_os = "android")] #[allow(non_snake_case)] pub mod android { extern crate jni; use self::jni::JNIEnv; use self::jni::objects::{JClass, JString, JObjectArray, JObject}; use self::jni::sys::{jstring, jboolean, jobject}; #[no_mangle] pub extern "system" fn Java_eu_mycompany_rust_MyPackage_checkAnswer<'local>( mut env: JNIEnv<'local>, _class: JClass<'local>, answerInput: JString<'local>, answerList: JObjectArray<'local>, ) -> jobject { // 1. 解析输入参数为Rust类型 let answer_input: String = env.get_string(&answerInput).expect("Failed to get input string").into(); let answer_list = kotlin_list_to_rust_vec_string(&mut env, &answerList); // 2. 执行核心业务逻辑 let (is_correct, corrected_answer) = answer_in_answer_list(&answer_input, &answer_list); // 3. 将布尔值转为Java Boolean包装对象 let j_is_correct: jboolean = if is_correct { 1 } else { 0 }; let j_is_correct_obj = env .call_static_method( "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", &[j_is_correct.into()], ) .expect("Failed to call Boolean.valueOf()") .l() .expect("Boolean.valueOf() returned null"); // 4. 将Rust字符串转为Java String let j_corrected_answer = match corrected_answer { Some(s) => env.new_string(&s).expect("Failed to create Java string"), None => env.new_string("").expect("Failed to create empty Java string") }; // 5. 创建Android Pair对象并返回 let result_pair = env .new_object( "android/util/Pair", "(Ljava/lang/Object;Ljava/lang/Object;)V", &[j_is_correct_obj.into(), (&j_corrected_answer).into()], ) .expect("Failed to create Pair object"); result_pair.into_raw() } // 辅助函数:将Kotlin字符串数组转为Rust Vec<String> fn kotlin_list_to_rust_vec_string(env: &mut JNIEnv, array: &JObjectArray) -> Vec<String> { let len = env.get_array_length(array).unwrap(); (0..len) .map(|i| { let jstr: JString = env.get_object_array_element(array, i).unwrap().into(); env.get_string(&jstr).unwrap().into() }) .collect() } // 核心业务逻辑示例 fn answer_in_answer_list(answer_input: &String, answer_list: &Vec<String>) -> (bool, Option<&String>) { let is_correct = answer_list.contains(answer_input); let corrected_answer = if !is_correct { answer_list.first() } else { None }; (is_correct, corrected_answer) } }
关键注意事项
- 类型严格匹配:JNI函数返回类型必须与Kotlin声明的返回类型对应,返回Pair时必须返回
jobject。 - 基本类型包装:Java泛型容器(如Pair)仅支持Object类型,必须将基本类型(boolean、int等)转为对应的包装类(Boolean、Integer)。
- 异常处理优化:示例中使用
expect触发panic,生产环境可改为捕获错误并通过JNI抛出异常给Kotlin端处理,避免应用崩溃。
内容的提问来源于stack exchange,提问作者NumesSanguis
相关产品推荐
相关产品推荐

