美容养生连锁东莞网站建设常州做的网站的公司

news/2025/9/24 6:10:06/文章来源:
美容养生连锁东莞网站建设,常州做的网站的公司,80后陈某做盗版视频网站,小程序开发者近期工作中有Rust和Java互相调用需求#xff0c;这篇文章主要介绍如何用Rust通过JNI和Java进行交互#xff0c;还有记录一下开发过程中遇到的一些坑。 JNI简单来说是一套Java与其他语言互相调用的标准#xff0c;主要是C语言#xff0c;官方也提供了基于C的C接口。 既然是C… 近期工作中有Rust和Java互相调用需求这篇文章主要介绍如何用Rust通过JNI和Java进行交互还有记录一下开发过程中遇到的一些坑。 JNI简单来说是一套Java与其他语言互相调用的标准主要是C语言官方也提供了基于C的C接口。 既然是C语言接口那么理论上支持C ABI的语言都可以和Java语言互相调用Rust就是其中之一。 关于JNI的历史背景以及更详细的介绍可以参考官方文档 在Rust中和Java互相调用可以使用原始的JNI接口也就是自己声明JNI的C函数原型在Rust里按照C的方式去调用但这样写起来会很繁琐而且都是unsafe的操作 不过Rust社区里已经有人基于原始的JNI接口封装好了一套safe的接口crate的名字就叫jni用这个库来开发就方便多了 文中涉及的代码放在了这个github仓库 https://github.com/metaworm/rust-java-demo Rust JNI 工程配置 如果你熟悉Cargo和Maven可以跳过这一节直接看我提供的github源码即可 Rust工程配置 首先通过cargo new java-rust-demo创建一个rust工程 然后切换到工程目录cd java-rust-demo并编辑Cargo.toml修改类型为动态库、加上对 jni crate 的依赖 [package] name rust-java-demo version 0.1.0 edition 2021[lib] crate-type [cdylib][dependencies] jni {version 0.19} 重命名src目录下的main.rs为lib.rsRust库类型的工程编译入口为 lib.rs然后添加以下代码 use jni::objects::*; use jni::JNIEnv;#[no_mangle] pub unsafe extern C fn Java_pers_metaworm_RustJNI_init(env: JNIEnv, _class: JClass) {println!(rust-java-demo inited); } 然后执行cargo build构建生成的动态库默认会位于target/debug目录下我这里用的linux系统动态库文件名为librust_java_demo.so如果是Windows系统文件名为rust_java_demo.dll 这样我们第一个JNI函数就创建成功了 通过Java_pers_metaworm_RustJNI_init这个导出函数给了Java的pers.metaworm.RustJNI这个类提供了一个native的静态方法init 这里只是简单地打印了一句话后面会通过这个初始化函数添加更多的功能 Java工程配置 还是在这个工程目录里把Java部分的代码放在java这个目录下在其中创建pers/metaworm/RustJNI.java文件 package pers.metaworm;public class RustJNI {static {System.loadLibrary(rust_java_demo);}public static void main(String[] args) {init();}static native void init(); } 我们使用流行的 maven 工具来构建Java工程在项目根目录下创建 maven 的工程文件 pom.xml ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdpers.metaworm/groupIdartifactIdRustJNI/artifactIdversion1.0-SNAPSHOT/versionpropertiesexec.mainClasspers.metaworm.RustJNI/exec.mainClassmaven.compiler.source1.8/maven.compiler.sourcemaven.compiler.target1.8/maven.compiler.targetmaven.compiler.encodingUTF-8/maven.compiler.encoding/propertiesdependencies/dependenciesbuildsourceDirectoryjava/sourceDirectorypluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdversion2.4/versionconfigurationencodingUTF-8/encoding/configuration/plugin/plugins/build /project 运行 DMEO 工程 上面的工程配置弄好之后就可以使用cargo build命令构建Rust提供的JNI动态库mvn compile命令来编译Java代码 Rust和Java代码都编译好之后执行java -Djava.library.pathtarget/debug -classpath target/classes pers.metaworm.RustJNI来运行 其中-Djava.library.pathtarget/debug指定了我们JNI动态库所在的路径-classpath target/classes指定了Java代码的编译输出的类路径pers.metaworm.RustJNI是Java main方法所在的类 不出意外的话运行之后会在控制台输出init函数里打印的rust-java-demo inited Java调用Rust 接口声明 前面的Java_pers_metaworm_RustJNI_init函数已经展示了如何给Java暴露一个native方法即导出名称为Java_类完整路径_方法名的函数然后在Java对应的类里声明对应的native方法 拓展除了通过导出函数给Java提供native方法还可以通过 RegisterNatives 函数动态注册native方法对应的jni封装的函数为JNIEnv::register_native_methods一般动态注册会在JNI_Onload这个导出函数里执行jvm加载jni动态库时会执行这个函数(如果有的话) 当在Java里首次调用native方法时JVM就会寻找对应名称的导出的或者动态注册的native函数并将Java的native方法和Rust的函数关联起来如果JVM没找到对应的native函数则会报java.lang.UnsatisfiedLinkError异常 为了演示我们再添加一些代码来覆盖更多的交互场景 lib.rs use jni::objects::*; use jni::sys::{jint, jobject, jstring}; use jni::JNIEnv;#[no_mangle] pub unsafe extern C fn Java_pers_metaworm_RustJNI_addInt(env: JNIEnv,_class: JClass,a: jint,b: jint, ) - jint {a b }#[no_mangle] pub unsafe extern C fn Java_pers_metaworm_RustJNI_getThisField(env: JNIEnv,this: JObject,name: JString,sig: JString, ) - jobject {let result env.get_field(this,env.get_string(name).unwrap().to_string_lossy(),env.get_string(sig).unwrap().to_string_lossy(),).unwrap();result.l().unwrap().into_inner() } RustJNI.java package pers.metaworm;public class RustJNI {static {System.loadLibrary(rust_java_demo);}public static void main(String[] args) {init();System.out.println(test addInt: (addInt(1, 2) 3));RustJNI jni new RustJNI();System.out.println(test getThisField: (jni.getThisField(stringField, Ljava/lang/String;) jni.stringField));System.out.println(test success);}String stringField abc;static native void init();static native int addInt(int a, int b);native Object getThisField(String name, String sig); } 其中addInt方法接收两个int参数并返回相加的结果getThisField是一个实例native方法它获取this对象指定的字段并返回 参数传递 从上一节的例子里可以看到jni函数的第一个参数总是JNIEnv很多交互操作都需要通过这个对象来进行 第二个参数是类对象(静态native方法)或this对象(实例native方法) 从第三个参数开始每一个参数对应Java的native方法所声明的参数 对于基础的参数类型可以直接用use jni::sys::*提供的j开头的系列类型来声明类型对照表 Java 类型Native 类型类型描述booleanjbooleanunsigned 8 bitsbytejbytesigned 8 bitscharjcharunsigned 16 bitsshortjshortsigned 16 bitsintjintsigned 32 bitslongjlongsigned 64 bitsfloatjfloat32 bitsdoublejdouble64 bitsvoidvoidnot applicable 对于引用类型(复合类型/对象类型)可以统一用jni::objects::JObject声明JObject是对jobject的rust封装带有生命周期参数对于String类型也可以用 JString 来声明JString是对JObject的一层简单封装 抛异常 前面的Java_pers_metaworm_RustJNI_getThisField函数里用了很多unwrap这在生产环境中是非常危险的万一传了一个不存在的字段名就直接crash了所以我们改进一下这个函数让他支持抛异常出错的时候能让Java捕获到 #[no_mangle] pub unsafe extern C fn Java_pers_metaworm_RustJNI_getThisFieldSafely(env: JNIEnv,this: JObject,name: JString,sig: JString, ) - jobject {let result (|| {env.get_field(this,env.get_string(name)?.to_string_lossy(),env.get_string(sig)?.to_string_lossy(),)?.l()})();match result {Ok(res) res.into_inner(),Err(err) {env.exception_clear().expect(clear);env.throw_new(Ljava/lang/Exception;, format!({err:?})).expect(throw);std::ptr::null_mut()}} } Java层的测试代码为 try {System.out.println(test getThisFieldSafely: (jni.getThisFieldSafely(stringField, Ljava/lang/String;) jni.stringField));jni.getThisFieldSafely(fieldNotExists, Ljava/lang/String;);} catch (Exception e) {System.out.println(test getThisFieldSafely: catched exception: e.toString());} 通过env.throw_new(Ljava/lang/Exception;, format!({err:?}))抛出了一个异常从JNI函数返回后Java就会捕获到这个异常 代码里可以看到在抛异常之前调用了env.exception_clear()来清除异常这是因为前面的get_field已经抛出一个异常了当env里已经有一个异常的时候后续再调用env的函数都会失败这个异常也会继续传递到上层的Java调用者所以其实这里没有这两句直接返回null的话Java也可以捕获到异常但我们通过throw_new可以自定义异常类型及异常消息 这其实不是一个典型的场景典型的场景应该是Rust里的某个调用返回了Error然后通过抛异常的形式传递到Java层比如除0错误 #[no_mangle] pub unsafe extern C fn Java_pers_metaworm_RustJNI_divInt(env: JNIEnv,_class: JClass,a: jint,b: jint, ) - jint {if b 0 {env.throw_new(Ljava/lang/Exception;, divide zero).expect(throw);0} else {a / b} } Rust调用Java 创建对象、调用方法、访问字段... 下面用一段代码展示如何在Rust中创建Java对象、调用方法、获取字段、处理异常等常见用法 #[allow(non_snake_case)] fn call_java(env: JNIEnv) {match (|| {let File env.find_class(java/io/File)?;// 获取静态字段let separator env.get_static_field(File, separator, Ljava/lang/String;)?;let separator env.get_string(separator.l()?.into())?.to_string_lossy().to_string();println!(File.separator: {}, separator);assert_eq!(separator, format!({}, std::path::MAIN_SEPARATOR));// env.get_static_field_unchecked(class, field, ty)// 创建实例对象let file env.new_object(java/io/File,(Ljava/lang/String;)V,[JValue::Object(env.new_string()?.into())],)?;// 调用实例方法let abs env.call_method(file, getAbsolutePath, ()Ljava/lang/String;, [])?;let abs_path env.get_string(abs.l()?.into())?.to_string_lossy().to_string();println!(abs_path: {}, abs_path);jni::errors::Result::Ok(())})() {Ok(_) {}// 捕获异常Err(jni::errors::Error::JavaException) {let except env.exception_occurred().expect(exception_occurred);let err env.call_method(except, toString, ()Ljava/lang/String;, []).and_then(|e| Ok(env.get_string(e.l()?.into())?.to_string_lossy().to_string())).unwrap_or_default();env.exception_clear().expect(clear exception);println!(call java exception occurred: {err});}Err(err) {println!(call java error: {err:?});}} }#[no_mangle] pub unsafe extern C fn Java_pers_metaworm_RustJNI_callJava(env: JNIEnv) {println!(call java);call_java(env) } 总结一下常用的函数具体用法可以参考JNIEnv的文档 创建对象 new_object创建字符串对象 new_string调用方法 call_method call_static_method获取字段 get_field get_static_field修改字段 set_field set_static_field 要注意的是调用方法、创建对象等需要传一个方法类型签名这是因为Java支持方法重载同一个类里一个名称的函数可能有多个所以需要通过类型签名来区分类型签名的规则可以参考官方文档 异常处理 call_java函数展示了如何在Rust中处理Java的异常 通过JNIEnv对象动态获取字段或者调用方法都会返回一个jni::errors::Result类型对应的Error类型为jni::errors::Error如果Error是jni::errors::Error::JavaException则表明在JVM执行过程中某个地方抛出了异常这种情况下就可以用exception_occurred函数来获取异常对象进行处理然后调用exception_clear来清除异常如果再返回到Java便可以继续执行 在非Java线程中调用Java 从Java中调用的Rust代码本身就处于一个Java线程中第一个参数为JNIEnv对象Rust代码用这个对象和Java进行交互 实际应用场景中可能需要从一个非Java线程或者说我们自己的线程中去调用Java的方法但我们的线程没有JNIEnv对象这时就需要调用JavaVM::attach_current_thread函数将当前线程附加到JVM上来获得一个JNIEnv #[no_mangle] pub unsafe extern C fn Java_pers_metaworm_RustJNI_callJavaThread(env: JNIEnv) {let vm env.get_java_vm().expect(get jvm);std::thread::spawn(move || {println!(call java in another thread);let env vm.attach_current_thread().expect(attach);call_java(env);}); } attach_current_thread函数返回一个AttachGuard对象可以解引用为JNIEnv并且在作用域结束drop的时候自动调用detach_current_thread函数原始的AttachCurrentThreadJNI函数如果当前线程已经attach了则会抛异常jni crate里的JavaVM::attach_current_thread做了一层封装如果当前已经attach了则会返回之前attach的对象保证不会重复attach JavaVM对象通过JNIEnv::get_java_vm函数获取可以在初始化的时候将这个变量存起来给后续的其他线程使用 局部引用、全局引用与对象缓存 关于局部引用与全局引用的官方文档 Rust提供的native函数传过来的对象引用都是局部引用局部引用只在本次调用JNI调用范围内有效而且不能跨线程使用如果跨线程必须使用全局引用 可以通过JNIEnv::new_global_ref来获取JClass、JObject的全局引用这个函数返回一个GlobalRef对象可以通过GlobalRef::as_object转成JObject或者JClass等对象GlobalRef对象drop的时候会调用DeleteGlobalRef将JVM内部的引用删除 前面的代码从Rust调用Java方法都是通过名称加方法签名调用的这种方式写起来很舒服但运行效率肯定是非常低的因为每次都要通过名称去查找对应的方法 其实JNI原始的C接口是通过jobjectID、jclassID、jmethodID、jfieldID来和Java交互的只不过是jni crate给封装了一层比较友好的接口 如果我们对性能要求比较高则可以在初始化的时候获取一些JClass、JObject的全局引用缓存起来后面再转成JClass、JObject来使用千万不要对jmethodID、jfieldID获取全局引用因为这俩都是通过jclassID生成的其声明周期和jclassID对应的对象相同不是需要GC的对象如果对jmethodID获取全局引用然后调用会导致某些JVM Crash对于jmethodID、jfieldID则可以基于JClass、JObject的全局引用获取后面直接使用即可 获取到这些全局的ID之后就可以通过JNIEnv::call_method_unchecked系列函数来更高效地调用Java 我用Rust强大的宏实现了这个过程可以让我们直接在Rust中以声明的方式缓存的所需类及其方法ID #[allow(non_snake_case)] pub mod cache {use anyhow::Context;use jni::errors::Result as JniResult;use jni::objects::*;use jni::JNIEnv;pub fn method_global_refa(env: JNIEnva,class: JClass,name: str,sig: str,) - JniResultJMethodIDa {let method env.get_method_id(class, name, sig)?.into_inner();Ok(JMethodID::from(method.cast()))}pub fn static_method_global_refa(env: JNIEnva,class: JClass,name: str,sig: str,) - ::jni::errors::ResultJStaticMethodIDa {let method env.get_static_method_id(class, name, sig)?.into_inner();Ok(JStaticMethodID::from(method.cast()))}macro_rules! gen_global_ref {(method_type) { JMethodIDstatic };(method_type static) { JStaticMethodIDstatic };(method_ref) { method_global_ref };(method_ref static) { static_method_global_ref };($(#[name $classname:literal]class $name:ident {$($method:ident : $($modify:ident)* $sig:literal,)*})*) {$(#[allow(non_snake_case)]pub struct $name {pub class: JClassstatic,$(pub $method: gen_global_ref!(method_type $($modify)*),)*}impl $name {pub fn from_env(env: JNIEnvstatic) - anyhow::ResultSelf {Self::from_class(env, env.find_class($classname)?)}pub fn from_class(env: JNIEnvstatic, class: JClass) - anyhow::ResultSelf {let cls env.new_global_ref(class)?;let class JClass::from(*cls.as_obj());core::mem::forget(cls);Ok(Self {class,$($method: gen_global_ref!(method_ref $($modify)*)(env, class, stringify!($method), $sig).context(stringify!($method))?,)*})}}// TODO: impl Drop)*pub struct CachedClasses {$(pub $name: $name,)*}impl CachedClasses {pub fn from_env(env: JNIEnvstatic) - anyhow::ResultSelf {Ok(Self {$($name: $name::from_env(env).context(stringify!($name))?,)*})}}unsafe impl Sync for CachedClasses {}unsafe impl Send for CachedClasses {}}}gen_global_ref! {#[name java/lang/Thread]class Thread {currentThread: static ()Ljava/lang/Thread;,getStackTrace: ()[Ljava/lang/StackTraceElement;,}#[name java/lang/StackTraceElement]class StackTraceElement {getLineNumber: ()I,toString: ()Ljava/lang/String;,}#[name java/io/File]class File {getAbsolutePath: ()Ljava/lang/String;,}}static mut CLASSES: OptionBoxCachedClasses None;pub unsafe fn init(env: JNIEnvstatic) - anyhow::ResultOptionBoxCachedClasses {Ok(CLASSES.replace(CachedClasses::from_env(env)?.into()))}pub fn get() - static CachedClasses {unsafe { CLASSES.as_ref().expect(Cached Java Classed not inited) }} }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/915041.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用jsp做的网站源代码网站ip访问做图表

嗨,我正面临这个问题我从RESTCall获取了一个URL网址是http://hck.re/kWWxUI但是当我在浏览器中检查时,它会重定向到https://s3-ap-southeast-1.amazonaws.com/he-public-data/afreen2ac5a33.jpg如何将此图像加载到我的imageView中我已经知道如何将毕加索…

网站建设开发方式包括哪些百度竞价推广登陆

python和多路复用的小栗子 Python 实现的多路复用多路复用如何知道fd就绪了?如何优化时间?优化事件处理 fd的状态有哪些? Python 实现的多路复用 # 导入selectors模块,这个模块可以实现I/O多路复用 import selectors # 导入s…

做网站建设的网络公司经营范围怎样填东莞网站推广大全

Crow:基于req.rul查找路由Rule对象及匹配参数-CSDN博客 介绍了当接收到http请求后如何查找到Rule对象 Connection::do_read -> HTTPParser::feed -> 而feed实际上会依此调用定义于http_parser_settings中的所有函数,并完成http信息的解析 const static http_parser_se…

网站开发设计流程图网站开发公司会计

原理就是本来可能要10台物理机完成的事现在只要5台,分别在每台物理机上虚拟一台,这5太虚拟机共享一个stronge,比如有一台物理机down掉后或是要做维护,我们可以把它上面的虚拟机牵走,从而减少损失或防止终端业务&#x…

河南 网站备案为什么自己做的网站用QQ打不开

在使用 Elasticsearch 时,我们总有需要修改索引映射的时候,这时我们只能进行 _reindex。事实上,这是一个相当昂贵的操作,因为根据数据量和分片数量,完整复制一个索引可能需要几个小时。 花费的时间不是大问题,但更严重的是,它会影响生产环境的性能甚至功能。 相信大家…

泰州做网站软件河北网站制作公司哪家专业

老早之前的计算机只有一个处理器,而 一个处理器在同一时刻只能处理一条指令 ,换句话说,我们的代码需要一行一行的按顺序被计算机执行,计算机只能把一个程序完整的执行完,然后再执行第二个程序。所以计算机专业的同学们…

网站建设 质量标准成都网站建设公司哪家专业

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦!!! 现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everythi…

域名备案关闭网站wordpress采集淘宝商品

技术:springbootmysqlvue 一、背景 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统同城上门喂遛宠物…

国内做新闻比较好的网站有哪些网站建设的作用和意义

一、tslib介绍 tslib 是专门为触摸屏设备所开发的 Linux 应用层函数库,并且是开源。 tslib 为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏 struct input_event 类型数据(这是输入设备上报给应用层的原始数据)并进行…

企业展示网站 价钱网站手机端打不开

目录 修改约束 创建数据库 添加约束 删除约束 Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 修改约束 如果说表结构的修改还在可以容忍的范畴之内,那么约束的修改是绝对 100% 禁止的 所有的约束一定要在…

国内p2p网站建设大兴模版网站开发公司哪家好

SVN 一、SVN简介1. 概念1.1 repository(源代码库)1.2 Checkout(提取)1.3 Commit(提交)1.4 Update (更新) 2. SVN的主要功能2.1 目录版本控制2.2 真实的版本历史2.3 自动提交2.4 纳入版本控管的元数据2.5 选…

网站模板的制作怎么做小程序开发步骤大全

朋友们、伙计们,我们又见面了,本期来给大家解读一下LeetCode中的第141道单链表OJ题,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! 数据结构与算法专栏:数据结构与算法 个 …

吴忠市建设局官方网站做网站开发能挣钱

前言 这里是【使用Python编写游戏辅助工具】的第三篇:鼠标连击器的实现。本文主要介绍使用Python来实现鼠标连击功能。 鼠标连击是指在很短的时间内多次点击鼠标按钮,通常是鼠标左键。当触发鼠标连击时,鼠标按钮会迅速按下和释放多次&#xf…

网站搭建和网站开发免费咨询医生在线男科

源码说明后台号码admin后台密码123456声明:该源码仅供学习出现,修复者不承担任何责任,下载安装即代表使用者自行承担责任源码安装方法需要服务器开启g11插件PHP5.6,主机用户推荐95云主机,因为已经开启所有扩展插件必须…

智能自助建站系统源码电子 东莞网站建设

一、安装python并配置环境变量 1、打开python官网,下载并安装 Welcome to Python.org 下载 寻找版本:推荐使用3.9版本,或其他表中显示为安全(security)的版本 安装:(略) 2、配置环…

柳州建设网官方网站最优惠的手机网站建设

发布时间:2023-7-20 主要更新内容: 1.增加3D地图功能 2.增加水球图 3.增加扇形图,在数据大屏 - 自定义组件中定义。 4.增加指标引导线功能,在数据大屏 - 自定义组件中定义。 5.详情页增加回调函数功能。 6.大屏/仪表盘模版下载,…

tiktok跨境电商怎么做锦州网站seo

目录标题 前言1、理论知识2、串口下载具体操作2.1、硬件准备2.2、软件准备2.3、设置单片机的启动模式为系统存储器启动2.4、软件配置2.5、下载程序 附:生成hex文件 前言 使用调试器下载程序又快有稳定还能使用调试功能,当然是下载调试的首选。但是拓展下串口下载程…

如何建设个人网站和博客网站建设行业知乎

目录 前言 一、REDIS概述 二、REDIS安装 1、编译安装 2.yum安装 三、Redis的目录结构 四、基础命令解析 五、在一台服务器上启动多个redis 六、数据库的基本操作 (一)登录数据库 (二)基础命令 七、Redis持久化 (一&…

网站如何做传输网盘网站面包屑怎么做

NO.1 Java.alng.NullPointerException这个异常大家肯定都经常遇到,异常的解释是 “程序遇上了空指针 “,简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作中,比如图…

在线营销单页网站制作微信公众号小程序助手

linux 改java堆内存大小[2021-02-08 23:06:29] 简介:linux查内存大小的方法:首先打开应用程序;然后选择系统工具选项,并单击系统终端选项;接着在系统终端命令行输入【free -h】命令;最后在结果中找到total即可查看到系…