gpt4 book ai didi

rust - 如何通过 JNI 从 Rust 调用 Java 方法?

转载 作者:行者123 更新时间:2023-11-29 08:23:14 28 4
gpt4 key购买 nike

我有一个带有 com.purplefrog.batikExperiment.ToPixels 类的 Java 库,它有一个方法 static void renderToPixelsShape3(int width, int height, byte[] rgbs)。需要什么 Rust 代码来调用 Java 方法并访问新填充的 rgbs 数组?

我打算从 Rust main() 函数调用 ToPixels.renderToPixelsShape3,因此 Rust 代码必须构建 JNI 环境。

最佳答案

这是一个简单的单文件项目,用于演示如何使用 jni crate:

Java 端

package org.example.mcve.standalone;

public class Mcve {
static {
System.load("/Users/svetlin/CLionProjects/mcve/target/debug/libmcve.dylib");
}

public static void main(String[] args) throws Exception {
doStuffInNative();
}

public static native void doStuffInNative();

public static void callback() {
System.out.println("Called From JNI");
}
}
  1. 在启动时加载 native 库。我正在使用需要绝对路径的 load。或者,您可以使用 loadLibrary,它只需要库的名称,但另一方面要求它位于特定位置。

  2. 为了能够从 Java 调用 native 方法,您必须找到要在您的库中使用的签名。为此,您必须生成一个 C 头文件。这可以通过以下方式完成:

cd src/main/java/org/example/mcve/standalone/

javac -h Mcve.java

结果你应该得到一个看起来像这样的文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_example_mcve_standalone_Mcve */

#ifndef _Included_org_example_mcve_standalone_Mcve
#define _Included_org_example_mcve_standalone_Mcve
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_example_mcve_standalone_Mcve
* Method: doStuffInNative
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_org_example_mcve_standalone_Mcve_doStuffInNative
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

rust 面

现在我们知道了所需的方法签名,我们可以创建我们的 Rust 库了!首先用 crate_type = "cdylib" 创建一个 Cargo.toml:

[package]
name = "mcve"
version = "0.1.0"
authors = ["Svetlin Zarev <svetlin.zarev@hidden.com>"]
edition = "2018"

[dependencies]
jni = "0.12.3"

[lib]
crate_type = ["cdylib"]

然后添加一个lib.rs文件,内容如下:

use jni::objects::JClass;
use jni::JNIEnv;

#[no_mangle]
#[allow(non_snake_case)]
pub extern "system" fn Java_org_example_mcve_standalone_Mcve_doStuffInNative(
env: JNIEnv,
_class: JClass,
) {
let class = env
.find_class("org/example/mcve/standalone/Mcve")
.expect("Failed to load the target class");
let result = env.call_static_method(class, "callback", "()V", &[]);

result.map_err(|e| e.to_string()).unwrap();
}

请注意,我们使用了生成的头文件中丑陋的方法名称和签名。否则 JVM 将无法找到我们的方法。

首先我们加载所需的类。在这种情况下,它并不是真正必要的,因为我们将同一个类作为名为 _class 的参数传递。然后我们使用作为参数收到的 env 调用所需的 java 方法。

第一个参数是目标类。

第二个 - 目标方法名称。

第三个 - 描述参数类型和返回值:(arguments)return-type。您可以找到更多关于奇特语法和神秘字母的信息 here在我们的例子中,我们没有任何参数,返回类型是 V,这意味着 VOID

第四个 - 包含实际参数的数组。由于该方法不期望任何内容,因此我们传递了一个空数组。

现在构建 Rust 库,然后运行 ​​Java 应用程序。因此,您必须在终端中看到 Called From JNI

在 Rust 中从 main() 调用 Java

首先,您必须生成一个 JVM 实例。您必须使用 jni crate 上的“调用”功能:

[dependencies.jni]
version = "0.12.3"
features = ["invocation", "default"]

您可能希望使用 .option() 自定义 jvm 设置:

fn main() {
let jvm_args = InitArgsBuilder::new()
.version(JNIVersion::V8)
.option("-Xcheck:jni")
.build()
.unwrap();

let jvm = JavaVM::new(jvm_args).unwrap();
let guard = jvm.attach_current_thread().unwrap();

let system = guard.find_class("java/lang/System").unwrap();
let print_stream = guard.find_class("java/io/PrintStream").unwrap();

let out = guard
.get_static_field(system, "out", "Ljava/io/PrintStream;")
.unwrap();

if let JValue::Object(out) = out {
let message = guard.new_string("Hello World").unwrap();
guard
.call_method(
out,
"println",
"(Ljava/lang/String;)V",
&[JValue::Object(message.into())],
)
.unwrap();
}
}

一切都一样,除了我们现在使用 AttachGuard 来调用 Java 方法而不是传递的 JNIEnv 对象。

这里棘手的部分是在启动 Rust 应用程序之前正确设置 LD_LIBRARY_PATH 环境变量,否则将无法找到 libjvm.so。在我的例子中是:

export LD_LIBRARY_PATH=/usr/lib/jvm/java-1.11.0-openjdk-amd64/lib/server/

但是你系统上的路径可能不同

关于rust - 如何通过 JNI 从 Rust 调用 Java 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56821728/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com