一文解秘Rust如何与Java互操作

打印 上一主题 下一主题

主题 912|帖子 912|积分 2736


本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议。转载请注明来自 唯你
利用场景

JAVA 与 Rust 互操作让 Rust 可以背靠 Java 大生态来做更多事情,而 Java 也可以享受 Rust 语言特性的内存安全,所有权机制,无畏并发。
互操作的典型场景包括:

  • 性能优化:利用 Rust 处置惩罚盘算密集型任务,提高 Java 应用的团体性能。
  • 体系级编程:结合 Rust 的底层控制能力与 Java 的高级抽象,实现更高效的体系交互。
  • 跨平台开辟:利用 Rust 编写核心逻辑,通过 JNI 在差异平台上与 Java 交互,实现高效跨平台开辟。
  • 安全关键应用:在金融、医疗等领域,利用 Rust 处置惩罚敏感数据和核心功能,保证高度安全性。
  • 实时体系:在游戏引擎、音频处置惩罚等延迟敏感的应用中,利用 Rust 处置惩罚时间关键部分。
背景知识

JNI



  • 全称 Java Native Interface,它答应 Java 代码与其他语言(如 C 或 C++)编写的应用程序进行互操作。
  • JNI Specification:这是 JNI 的官方规范,详细形貌了 JNI 的利用方法、接口和功能。
Java 虚拟机(JVM)


JNI 是 Java 虚拟机的一部分,JVM 在启动时为每个线程创建一个 JNI 环境。JNI 环境包括指向 JVM 内部数据布局的指针,这些数据布局用于存储 Java 对象、方法和字段的信息。
JNIEnv (JNI 环境)



  • JNIEnv是一个指向布局体的指针,代表当火线程的 JNI 环境。它包罗所有 JNI 相干函数的指针,让你能在当地代码中利用这些函数。每个线程都有自己独立的JNIEnv,所以不能在差异线程间转达这个指针。
  • 可以将JNIEnv视为一个"翻译器"。当 Rust 代码需要与 Java 交互时,它通过这个"翻译器"发送哀求,当调用 Java 方法或获取 Java 对象的属性。每个线程都拥有自己独立的"翻译器",这确保了各线程与 Java 交互时的独立性。
另外


  • 当 Java 代码调用当地方法时,JVM 会加载相应的当地库并创建一个JNIEnv指针。
  • 当地代码可以利用这个指针访问 JNI 提供的函数,进行 Java 对象的操作。
  • 每个线程有独立JNIEnv,保证线程安全。新线程需调用AttachCurrentThread获取对应JNIEnv。
  • JNI 提供了数据类型转换机制,实现 Java 与 C/C++之间的数据转达。
在 Rust 生态中利用 jni 0.21.1 库可以实现与 Java 代码的交互。
JNI 0.21.1 简介

该项目为 Rust 提供了完备的 JNI 绑定,答应:

  • 利用 Rust 代码与 Java 库进行交互,调用 Java 方法和访问 Java 对象。
  • 从 Rust 代码中利用 Java 类和接口。
  • 实现跨语言的高效数据互换。
  • 利用 Rust 的性能上风和 Java 的成熟生态体系
跨平台 UI 框架 Flutter 源码中的 MethodChannel 实现了 Dart 与 Android 层的通信,其底层 C++也是通过 JNI 调用插件中的 onMethodCall 来实现的。这与上述 jni 0.21.1 采用了相同的思绪,但存在以下差异点:

  • 语言特性和类型安全:
    Rust 的jni库提供了一种更安全的方式来处置惩罚 Java 对象和方法调用。它利用 Rust 的所有权体系来镌汰潜在的内存错误,使得在 Rust 中利用 JNI 时更易于管理资源和避免常见错误。
  • 多平台支持:jni 0.21.1 提供了更广泛的跨平台支持。
如何运行示例

示例源码请阅读原文,见原文底部源码获取
在 Windows 11 环境下运行示例时,笔者遇到了两个题目:

  • Windows 自带的 PowerShell 无法直接实行 Makefile
  • 由于 Rust 配置了特定目标平台,出现了不明缘故原由的编译错误
以下是解决这些题目的方法:
在 MinGW-w64 中实行 Makefile


  • 确保已在 MinGW-w64 环境中安装 mingw32-make 工具(通常随 MinGW-w64 一起安装)
  • 打开 MinGW-w64 命令行
  • 导航至 Makefile 所在目录
  • 实行以下命令
  1. //当前示例中是makefile,直接执行mingw32-make -f makefile即可
  2. mingw32-make -f YourMakefileName
复制代码
确认当前 Rust 环境

举例来说,笔者在 C:\Users\xxx.cargo 目录下配置了 config.toml 文件:
  1. [build]
  2. target = "aarch64-linux-android"
复制代码
这导致在 Windows 上利用 mingw32-make 来编译针对 Android 平台的 Rust .so 文件,造成了混乱并引发了莫名其妙的编译错误。解决方法是删除不须要的 config.toml 文件,确保当前运行环境与目标平台(如 Windows)一致。
输出结果:
  1. Hello, josh!
  2. [B@2f92e0f4
  3. factCallback: res = 720
  4. counterCallback: count = 1
  5. counterCallback: count = 2
  6. counterCallback: count = 3
  7. counterCallback: count = 4
  8. counterCallback: count = 5
  9. Invoking asyncComputation (thread id = 1)
  10. asyncCallback: thread id = 23, progress = 0%
  11. asyncCallback: thread id = 23, progress = 10%
  12. asyncCallback: thread id = 23, progress = 20%
  13. asyncCallback: thread id = 23, progress = 30%
  14. asyncCallback: thread id = 23, progress = 40%
  15. asyncCallback: thread id = 23, progress = 50%
  16. asyncCallback: thread id = 23, progress = 60%
  17. asyncCallback: thread id = 23, progress = 70%
  18. asyncCallback: thread id = 23, progress = 80%
  19. asyncCallback: thread id = 23, progress = 90%
  20. asyncCallback: thread id = 23, progress = 100%
复制代码
示例简析

让我们深入分析示例中 asyncComputation 的流程。其核心目的是在 Rust 端实行一个异步盘算,同时 Rust 端会调用 Java 端来报告盘算进度。

团体流程图说明:

  • Java 的 main()方法调用 asyncComputation()。
  • asyncComputation()通过 JNI 调用 Rust 的 Java_HelloWorld_asyncComputation()函数。
  • Rust 函数创建一个新线程来实行异步盘算。
  • 在新线程中,Rust 实行盘算并周期性地调用 Java 的 asyncCallback()方法报告进度。
  • 当 Rust 完成盘算后,控制权返回到 Java 的主线程。
这个过程展示了 Java 调用 Rust(步骤 1)和 Rust 回调 Java(步骤 4)的双向交互。
源码如下


1 将一个 HelloWorld 类实例转达给 Rust 端,这对应下方 Rust 侧实现中 #3 处的 callback 对象


Rust 实现说明


  • JNIEnv 参数:详见上述相干概念中的解释。
  • JClass:代表调用此当地方法的 Java 类引用,主要用于访问类级别的静态方法和字段。
  • callback:Java 中新创建的 HelloWorld 对象实例。
  • 获取 JVM 对象:因为 env 对象不支持线程间转达和共享(仅实现了 Send),而 JVM 支持。通过 JVM 对象可在线程间转达,并终极得到 env。
  • 创建全局引用:获取 HelloWorld() 实例对象的全局引用,防止被垃圾回收。
  • 线程安全:每个线程都有自己的 JNIEnv,确保线程安全。在新线程中需调用 AttachCurrentThread 获取对应的 JNIEnv。
  • 反向调用 Java:通过 env 反向调用 Java 代码。调用对象是新创建的 HelloWorld 实例,回调方法是其中的 asyncCallback。在 JNI 中,"(I)V" 是方法签名,形貌了 Java 方法的参数和返回类型。"(I)V" 表现接受一个整数参数并返回 void 的方法。在这里,asyncCallback 方法接收一个整数(progress)作为参数,无返回值。
总结


  • Java 与 Rust 互操作让两种语言上风互补,提高性能和安全性,适用于多种场景如性能优化、体系级编程和跨平台开辟。
  • JNI(Java Native Interface)是实现 Java 与 Rust 互操作的关键技能,答应 Java 代码与其他语言编写的应用程序进行交互。
  • 通过示例分析,我们了解了 Java 调用 Rust 函数和 Rust 回调 Java 方法的双向交互过程,展示了两种语言之间的无缝协作。
参考链接

https://github.com/jni-rs/jni-rs
JNI APIs and Developer Guides
Leveraging Rust in our high-performance Java database

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

尚未崩坏

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表