Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/image/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何通过JNI从Rust调用Java方法?_Rust_Java Native Interface - Fatal编程技术网

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

如何通过JNI从Rust调用Java方法?,rust,java-native-interface,Rust,Java Native Interface,我有一个Java库,它有一个类com.purplefrog.batikeExperiment.ToPixels,它有一个方法static void renderToPixelsShape3(int-width,int-height,byte[]rgbs)。调用Java方法并访问新填充的rgbs数组需要什么代码 我打算从Rustmain()函数调用ToPixels.renderToPixelsShape3,因此Rust代码必须构建JNI环境。下面是一个简单的单文件项目,演示如何使用JNI机箱: [

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


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

下面是一个简单的单文件项目,演示如何使用JNI机箱:

[dependencies.jni]
version = "0.12.3"
features = ["invocation", "default"]
Java端
package org.example.mcve.standalone;
公共级Mcve{
静止的{
load(“/Users/svetlin/CLionProjects/mcve/target/debug/libmcve.dylib”);
}
公共静态void main(字符串[]args)引发异常{
dostuffinantive();
}
公共静态本机void dostuffinative();
公共静态无效回调(){
System.out.println(“从JNI调用”);
}
}
  • 启动时加载本机库。我正在使用
    load
    ,它需要一个绝对路径。或者,您可以使用
    loadLibrary
    ,它只需要库的名称,但另一方面要求它位于特定位置

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

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

    javac-hmcve.java

    因此,您应该得到一个如下所示的文件

    /*不要编辑此文件-它是机器生成的*/
    #包括
    /*类组织的标题\u示例\u mcve\u独立\u mcve*/
    #ifndef包括组织示例独立示例
    #定义(包含)(组织)(示例)(独立)(示例)
    #ifdef_uucplusplus
    外部“C”{
    #恩迪夫
    /*
    *类别:组织\u示例\u mcve\u独立\u mcve
    *方法:剂量法
    *签字:()五
    */
    JNIEXPORT void JNICALL Java_org_示例_mcve_standalone_mcve_dostuffinanative
    (JNIEnv*,jclass);
    #ifdef_uucplusplus
    }
    #恩迪夫
    #恩迪夫
    
    锈面 既然我们知道了所需的方法签名,我们就可以创建我们的Rust库了!首先使用
    板条箱创建Cargo.toml\u type=“cdylib”

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

    首先,我们加载所需的类。在本例中,这并不是必需的,因为我们传递了与名为
    \u class
    的参数相同的类。然后,我们使用作为参数接收的
    env
    调用所需的java方法

    第一个参数是目标类

    第二个-目标方法名

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

    第四个-包含实际参数的数组。由于该方法不需要任何值,因此我们传递一个空数组

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

    在Rust中从
    main()
    调用Java 首先,您必须生成一个JVM实例。您必须在jni板条箱上使用“调用”功能:

    [dependencies.jni]
    version = "0.12.3"
    features = ["invocation", "default"]
    
    您可能希望使用
    .option()
    自定义jvm设置:

    fn main(){
    让jvm_args=InitArgsBuilder::new()
    .version(JNIVersion::V8)
    .option(“-Xcheck:jni”)
    .build()
    .unwrap();
    让jvm=JavaVM::new(jvm_args).unwrap();
    让guard=jvm.attach_current_thread().unwrap();
    让system=guard.find_类(“java/lang/system”).unwrap();
    让print_stream=guard.find_类(“java/io/PrintStream”).unwrap();
    放出
    .get_static_字段(系统,“out”,“Ljava/io/PrintStream;”)
    .unwrap();
    如果让JValue::Object(out)=out{
    让message=guard.new_字符串(“Hello World”).unwrap();
    警卫
    .call_方法(
    出来
    “println”,
    “(Ljava/lang/String;)V”,
    &[JValue::Object(message.into())],
    )
    .unwrap();
    }
    }
    
    一切都是一样的,只是我们现在使用
    AttachGuard
    来调用Java方法,而不是传递的
    JNIEnv
    对象

    这里棘手的部分是在启动Rust应用程序之前正确设置
    LD\u LIBRARY\u PATH
    环境变量,否则它将无法找到libjvm.so。就我而言,它是:

    export-LD\u-LIBRARY\u-PATH=/usr/lib/jvm/java-1.11.0-openjdk-amd64/lib/server/


    但在你的系统中,路径可能不同

    以Svetlin Zarev的答案为起点,我已经设法对其进行了扩展,并找到了如何回答其余问题的方法。我不认为这是一个明确的答案,因为我预料还有缺点,因为我所做的一切都是用石头敲击,直到它起作用为止。 Cargo.toml是:

    [package]
    name = "rust_call_jni"
    version = "0.1.0"
    authors = ["Robert Forsman <git@thoth.purplefrog.com>"]
    edition = "2018"
    
    
    [dependencies.jni]
    version="0.12.3"
    features=["invocation"]
    
    我不能绝对肯定我是否正确地进行了阵列复制,但它似乎没有爆炸。更有经验的Rust/Java程序员可能会发现一些错误(并留下评论)

    为了结束这个毛球,让我们将字节写入一个文件,这样我们可以在GIMP中查看图像:

        {
            use std::fs::File;
            use std::path::Path;
            use std::io::Write;
            let mut f = File::create(Path::new("/tmp/x.ppm")).expect("why can't I create the image file?");
            f.write_all(format!("P6\n{} {} 255\n", width, height).as_bytes()).expect("failed to write image header");
            let tmp:&[u8] =unsafe { &*(rgbs3.as_slice() as *const _ as *const [u8])};
            f.write_all( tmp).expect("failed to write image payload");
            println!("wrote /tmp/x.ppm");
        }
    
        return Ok(());
    }
    
    请告诉我有更好的方法将
    Vec
    写入文件(因为这是谷歌搜索结果中显示的解决方案,但诉诸
    不安全的
    块让我很难过)

    我省略了henious_classpath()
    的定义,因为这只是类路径的大约30个jar的列表。我想知道一个妈妈
    let width = 400;
    let height = 400;
    let rgbs = env.new_byte_array(width*height*3)?;
    let rgbs2:JObject = JObject::from(rgbs);
    
    let result = je.call_static_method(cls, "renderToPixelsShape3", "(II[B)V", &[
        JValue::from(width),
        JValue::from(height),
        JValue::from(rgbs2),
    ])?;
    
    println!("{:?}", result);
    
    let blen = env.get_array_length(rgbs).unwrap() as usize;
    let mut rgbs3:Vec<i8> = vec![0; blen];
    println!("byte array length = {}", blen);
    
    env.get_byte_array_region(rgbs, 0, &mut rgbs3)?;
    
        {
            use std::fs::File;
            use std::path::Path;
            use std::io::Write;
            let mut f = File::create(Path::new("/tmp/x.ppm")).expect("why can't I create the image file?");
            f.write_all(format!("P6\n{} {} 255\n", width, height).as_bytes()).expect("failed to write image header");
            let tmp:&[u8] =unsafe { &*(rgbs3.as_slice() as *const _ as *const [u8])};
            f.write_all( tmp).expect("failed to write image payload");
            println!("wrote /tmp/x.ppm");
        }
    
        return Ok(());
    }
    
    use std::convert::TryFrom;
    use j4rs::{Instance, InvocationArg, Jvm, JvmBuilder};
    
    fn main() -> Result<(), J4RsError> {
        // Create a Jvm
        let jvm = JvmBuilder::new().build()?;
        // Create the values for the byte array
        let rgbs: Vec<InvocationArg> = [0i8; 400 * 400 * 3]
           .iter()
           .map(|r| InvocationArg::try_from(r).unwrap()
                    .into_primitive().unwrap())
           .collect();
    
        // Create a Java array from the above values
        let byte_arr = jvm.create_java_array("byte", &rgbs)?;
        // Invoke the static method
        jvm.invoke_static(
            "com.purplefrog.batikExperiment.ToPixels",
            "renderToPixelsShape3",
            &[
                InvocationArg::try_from(33_i32)?.into_primitive()?,
                InvocationArg::try_from(333_i32)?.into_primitive()?,
                InvocationArg::try_from(byte_arr)?
            ])?;
    
        Ok(())
    }