6个版本
0.2.2 | 2023年10月30日 |
---|---|
0.2.0 | 2021年8月7日 |
0.1.0 | 2021年4月7日 |
0.0.3 | 2020年9月21日 |
#221 in FFI
2,128次每月下载
在robusta_jni中使用
115KB
2.5K SLoC
robusta — Rust和Java之间的简单互操作
此库提供了一个过程宏,使在Rust中编写与JNI兼容的代码更加容易。
它可以执行Rust输入和输出类型的自动转换(见限制)。
[dependencies]
robusta_jni = "0.2"
使用方法
只需要在正确位置添加几个属性。
首先,在模块上添加一个#[bridge]
属性,使其能够被robusta
处理。
然后,我们需要为每个在Rust中实现的具有本地方法的类创建一个结构体,并且每个这样的结构体都必须用具有对应Java包名称的#[package]
属性进行注释。
之后,可以像编写普通的Rust函数一样编写实现的函数,宏将负责将标记为公共的函数和具有"jni"
ABI的函数转换为Java类型。默认情况下,如果转换失败,则会抛出Java异常。
另一方面,如果您需要从Rust调用Java函数,您需要添加一个"java"
ABI,并在self
/&self
/&mut self
(或如果方法是静态的,则为第一个参数)之后添加一个&JNIEnv
参数,并保持函数体为空。
在这些方法上,您可以附加一个call_type
属性来管理如何处理转换和错误:默认情况下,隐含了#[call_type(safe)]
,但您可以在任何时候切换到#[call_type(unchecked)]
,可能不需要更改少量或没有代码。
您还可以通过#[input_type]
属性强制输入参数使用Java类型,这对于Android JNI开发等可能很有用。
Android特定性
在Android应用程序中,要从Rust调用Java类,JVM使用调用栈来查找所需的类。但当一个Rust线程中时,您就不再有调用栈。
因此,为了能够调用Java类,您必须传递类引用而不是字符串类路径。
您可以在robusta-android-example/src/thread_func.rs
中找到此用法示例。
代码示例
您可以在./robusta-example
下找到示例。要运行它,您应该在您的PATH上具有java
和javac
,然后执行。
$ cd robusta-example
$ make java_run
# if you don't have `make` installed:
$ cargo build && javac com/example/robusta/HelloWorld.java && RUST_BACKTRACE=full java -Djava.library.path=../target/debug com.example.robusta.HelloWorld
Android上的使用示例
您可以在./robusta-android-example
下找到Robusta在Android上的使用示例。要运行它,使用Android Studio打开robustaAndroidExample项目。
Cargo构建由gradle自动运行。
Rust的lib.rs是Java类RobustaAndroidExample的镜像。
此示例仅获取应用程序授权的文件路径。
示例用法
Rust端
use robusta_jni::bridge;
use robusta_jni::convert::Signature;
#[bridge]
mod jni {
#[derive(Signature)]
#[package(com.example.robusta)]
struct HelloWorld;
impl HelloWorld {
pub extern "jni" fn special(mut input1: Vec<i32>, input2: i32) -> Vec<String> {
input1.push(input2);
input1.iter().map(ToString::to_string).collect()
}
}
}
Java端
package com.example.robusta;
import java.util.*;
class HelloWorld {
private static native ArrayList<String> special(ArrayList<Integer> input1, int input2);
static {
System.loadLibrary("robusta_example");
}
public static void main(String[] args) {
ArrayList<String> output = HelloWorld.special(new ArrayList<Integer>(List.of(1, 2, 3)), 4);
System.out.println(output)
}
}
类型转换细节和自定义类型的扩展
有四个特质控制Rust类型如何转换为Java类型:(Try)FromJavaValue
和(Try)IntoJavaValue
。
这些特质用于输入和输出类型,实现它们对于允许库执行自动类型转换是必要的。
这些特质使用了jni
包提供的类型,然而为了与robusta
提供最大兼容性,我们建议使用在robusta_jni::jni
下重新导出的版本。
引发异常
您可以通过返回一个包含jni::errors::Result
的Err
变体来让Rust本地方法引发Java异常。
转换表
Rust | Java |
---|---|
i32 | int |
bool | boolean |
char | char |
i8 | byte |
f32 | float |
f64 | double |
i64 | long |
i16 | short |
String | String |
Vec |
ArrayList |
Box<[u8]> | byte[] |
jni::JObject<'env> ‡ | (任何Java对象作为输入类型) |
jni::jobject | (任何Java对象作为输出) |
† 类型参数 T
必须实现适当的转换类型
‡ 特殊的 'env
生命周期 必须 使用
限制
目前转换机制存在一些限制
- 仅通过不透明的
JObject/jobject
类型支持装箱类型 - 自动类型转换仅限于上面概述的表格,尽管在需要时易于扩展。
贡献
我很乐意接受外部贡献! :)
依赖
~2.5MB
~50K SLoC