如何使用Rust的jni crate获取Java字段并进行交互?
如何使用Rust的jni crate获取Java字段并进行交互?
嘿,刚好我之前用jni crate折腾过类似的场景,给你一步步拆解怎么实现哈!
首先得明确你的Java类结构,比如你有FirstClass,里面包含一个自定义类类型的字段,现在要在Rust的native方法里拿到这个自定义类的实例,然后和它交互。我先理清楚核心流程,再放完整的代码示例。
核心步骤梳理
- 先拿到
FirstClass的实例对象(就是native方法参数里的obj) - 获取
FirstClass的类引用,这样才能找到它的字段信息 - 根据字段名和正确的字段签名拿到字段ID,这一步很容易踩坑,签名格式千万不能错
- 取出字段对应的自定义类实例对象
- 接下来就可以对这个自定义类实例做各种操作:读它的字段、调用它的方法都没问题
完整代码示例
先给你看Java这边的类结构(方便对应Rust代码):
// FirstClass.java package name.11nteresting.example; public class FirstClass { // 我们要获取的目标字段 private SecondClass mySecondClass; public FirstClass() { this.mySecondClass = new SecondClass(); } // 声明native方法 public native void example(); } // SecondClass.java package name.11nteresting.example; public class SecondClass { public int someInt = 42; public String someString = "Hello from Java!"; public void doSomething() { System.out.println("SecondClass's doSomething called!"); } }
然后是Rust这边的native方法实现:
use jni::{JNIEnv, JObject}; #[no_mangle] pub extern "system" fn Java_name_11nteresting_example_FirstClass_example<'local>( mut env: JNIEnv<'local>, obj: JObject<'local>, // 这个就是FirstClass的实例对象 ) { // 1. 获取FirstClass的类引用 let first_class = match env.get_object_class(obj) { Ok(class) => class, Err(e) => { eprintln!("获取FirstClass类引用失败: {:?}", e); return; } }; // 2. 获取mySecondClass字段的ID,注意签名格式:L完整包名/类名; let second_class_field_id = match env.get_field_id( first_class, "mySecondClass", "Lname/11nteresting/example/SecondClass;", ) { Ok(id) => id, Err(e) => { eprintln!("获取mySecondClass字段ID失败: {:?}", e); return; } }; // 3. 取出FirstClass实例中的mySecondClass字段值(也就是SecondClass的实例) let second_class_obj: JObject<'local> = match env.get_field(obj, second_class_field_id) { Ok(field_value) => match field_value.l() { Ok(obj) => obj, Err(e) => { eprintln!("字段不是有效的对象类型: {:?}", e); return; } }, Err(e) => { eprintln!("获取mySecondClass字段值失败: {:?}", e); return; } }; // --- 现在可以和SecondClass实例交互了,举两个例子 --- // 例子1: 获取SecondClass的someInt字段值 let second_class = match env.get_object_class(second_class_obj) { Ok(class) => class, Err(e) => { eprintln!("获取SecondClass类引用失败: {:?}", e); return; } }; let some_int_field_id = match env.get_field_id(second_class, "someInt", "I") { Ok(id) => id, Err(e) => { eprintln!("获取someInt字段ID失败: {:?}", e); return; } }; let some_int: i32 = match env.get_field(second_class_obj, some_int_field_id) { Ok(value) => match value.i() { Ok(int_val) => int_val, Err(e) => { eprintln!("someInt不是有效的int类型: {:?}", e); return; } }, Err(e) => { eprintln!("获取someInt字段值失败: {:?}", e); return; } }; println!("从SecondClass拿到的someInt值: {}", some_int); // 例子2: 调用SecondClass的doSomething方法 let do_something_method_id = match env.get_method_id(second_class, "doSomething", "()V") { Ok(id) => id, Err(e) => { eprintln!("获取doSomething方法ID失败: {:?}", e); return; } }; if let Err(e) = env.call_method(second_class_obj, do_something_method_id, &[]) { eprintln!("调用doSomething方法失败: {:?}", e); } }
几个容易踩坑的点要注意:
- 字段/方法签名必须准确:基本类型的签名比如int是
I,boolean是Z,String是Ljava/lang/String;;自定义类的签名是L完整包名/类名;(注意末尾的分号不能丢) - 错误处理要到位:JNI操作很容易因为签名错、对象为空等失败,实际项目里别全用expect,用match或者?(如果函数返回Result的话)处理更稳妥
- 函数命名规则:Rust里的native函数名要严格遵循JNI规范:
Java_包名_类名_方法名,包名里的.要换成_,如果包名/类名有数字开头(比如你这里的11nteresting),要转成_1,确保和Java里的native方法完全对应 - 生命周期:注意
'local生命周期,确保所有JNI对象引用都在JNIEnv的生命周期内有效,避免悬垂引用
备注:内容来源于stack exchange,提问作者An1nterestingName
相关产品推荐
相关产品推荐

