5 个不稳定版本
0.2.2 | 2023 年 10 月 30 日 |
---|---|
0.2.1 | 2023 年 3 月 13 日 |
0.2.0 | 2021 年 8 月 7 日 |
0.1.0 | 2021 年 4 月 7 日 |
0.0.3 | 2020 年 9 月 21 日 |
#47 in FFI
2,242 每月下载量
88KB
910 行
包含 (JAR 文件, 59KB) gradle-wrapper.jar
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 的镜像。
此示例仅获取 App 授权路径的文件。
示例用法
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
crate 提供的类型,然而为了与 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<T>† | ArrayList<T> |
Box<[u8]> | byte[] |
jni::JObject<'env> ‡ | (任何 Java 对象作为输入类型) |
jni::jobject | (任何 Java 对象作为输出) |
† 类型参数 T
必须实现适当的转换类型
‡ 特殊的 'env
生命周期 必须 使用
限制
目前转换机制存在一些限制
- 仅通过不透明的
JObject
/jobject
类型支持装箱类型 - 自动类型转换仅限于上述表格中概述的内容,但如需扩展则很容易。
贡献
我很乐意接受外部贡献! :)
依赖项
~2.2–3.5MB
~62K SLoC