- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个带有 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:
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");
}
}
在启动时加载 native 库。我正在使用需要绝对路径的 load
。或者,您可以使用 loadLibrary
,它只需要库的名称,但另一方面要求它位于特定位置。
为了能够从 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 库了!首先用 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
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/
编辑备注 由于 Rust(版本:1.42)仍然没有稳定的 ABI ,推荐使用extern (目前相当于extern "C"(将来可能会改变))否则,可能需要重新编译库。 This article解释如
词法分析器/解析器文件位于 here非常大,我不确定它是否适合只检索 Rust 函数列表。也许我自己编写/使用另一个库是更好的选择? 最终目标是创建一种执行管理器。为了上下文化,它将能够读取包装在函数
我试图在 Rust 中展平 Enum 的向量,但我遇到了一些问题: enum Foo { A(i32), B(i32, i32), } fn main() { let vf =
我正在 64 位模式下运行的 Raspberry Pi 3 上使用 Rust 进行裸机编程。我已经实现了一个自旋锁,如下所示: use core::{sync::atomic::{AtomicBool
我无法理解以下示例是如何从 this code 中提炼出来的, 编译: trait A: B {} trait B {} impl B for T where T: A {} struct Foo;
在我写了一些代码和阅读了一些文章之后,我对 Rust 中的移动语义有点困惑,我认为值移动后,它应该被释放,内存应该是无效的。所以我尝试写一些代码来作证。 第一个例子 #[derive(Debug)]
https://doc.rust-lang.org/reference/types/closure.html#capture-modes struct SetVec { set: HashSe
考虑 const-generic 数据结构的经典示例:方矩阵。 struct Matrix { inner: [[T; N]; N] } 我想返回一个结构体,其 const 参数是动态定义的:
以下代码无法编译,因为 x在移动之后使用(因为 x 具有类型 &mut u8 ,它没有实现 Copy 特性) fn main() { let mut a: u8 = 1; let x:
我在玩 Rust,发现了下面的例子: fn main() { let mut x = [3, 4, 5].to_vec(); x; println!("{:?}", x); }
假设一个 Rust 2018 宏定义了一个 async里面的功能。它将使用的语法与 Rust 2015 不兼容。因此,如果您使用 2015 版编译您的 crate,那么宏中的扩展代码不会与它冲突吗?
假设我有一些 Foo 的自定义集合s: struct Bar {} struct Foo { bar: Bar } struct SubList { contents: Vec, }
代码如下: fn inner(x:&'a i32, _y:&'b i32) -> &'b i32 { x } fn main() { let a = 1; { let b
在lifetime_things的定义中,'b的生命周期比'a长,但实际上当我调用这个函数时,x1比y1长,但是这样可以编译成功: //here you could see 'b:'a means
我正在尝试检索 FLTK-RS Widget 周围的 Arc Mutex 包装器的内部值: pub struct ArcWidget(Arc>); impl ArcWidget{ pub
如下代码所示,我想封装一个定时函数,返回一个闭包的结果和执行时间。 use tap::prelude::Pipe; use std::time::{Instant, Duration}; pub fn
我想实现自己的通用容器,这是我正在使用的特征的片段: pub trait MyVec where Self: Default + Clone + IntoIterator, Self:
所需代码: 注释掉的块可以编译并工作,但是我想从嵌套的匹配样式转变为更简洁的函数链 async fn ws_req_resp(msg: String, conn: PgConn) -> Result>
我正在尝试编写一些代码,该代码将生成具有随机值的随机结构。对于结构,我具有以下特征和帮助程序宏: use rand::{thread_rng, Rng}; use std::fmt; pub trai
我有一个带有函数成员的结构: struct Foo { fun: Box, } type FooI = Foo; 这不起作用: error[E0106]: missing lifetime s
我是一名优秀的程序员,十分优秀!