如何通过JNI从同步Java函数调用Rust异步函数?
基于Rust JNI实现Java侧调用Rust异步函数的方案
你要的核心是在Rust侧的同步JNI导出函数内执行异步逻辑,直接借助Rust异步运行时的阻塞等待接口即可实现,具体实现步骤如下:
1 Rust侧核心实现
依赖配置
先在Cargo.toml中引入必要依赖:
[package] name = "evo" version = "0.1.0" edition = "2021" [dependencies] jni = "0.21" tokio = { version = "1.0", features = ["rt-multi-thread", "macros"] } once_cell = "1.18"
代码实现
用全局共享的异步运行时避免重复创建开销,同步JNI函数内调用block_on执行异步任务:
use jni::objects::{JClass, JObject}; use jni::sys::jobject; use jni::JNIEnv; use once_cell::sync::Lazy; use tokio::runtime::Runtime; // 全局异步运行时实例 static RUNTIME: Lazy<Runtime> = Lazy::new(|| { tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap() }); // 你的异步业务函数 async fn async_create_main_worker() -> MainWorker { // 这里放异步逻辑 MainWorker {} } // JNI导出的同步函数,对应Kotlin侧的create_main_worker外部方法 #[no_mangle] pub extern "system" fn Java_quest_evo_runtime_Library_create_1main_1worker( env: JNIEnv, _class: JClass, ) -> jobject { // 阻塞等待异步任务执行完成 let worker = RUNTIME.block_on(async_create_main_worker()); // 把Rust侧结构体转换成Java侧的MainWorker对象返回,这里按你的实际对象映射逻辑实现 let java_worker = env.new_object("quest/evo/runtime/objects/MainWorker", "()V", &[]).unwrap(); java_worker.into_raw() } // 示例用的Rust侧MainWorker结构体 struct MainWorker {}
2 Kotlin侧加载逻辑
你提供的本地库加载代码可以直接使用,格式化后如下:
package quest.evo.runtime import quest.evo.runtime.objects.MainWorker import java.io.File import java.io.FileOutputStream object Library { external fun create_main_worker(): MainWorker; init { val tempDir = File(System.getProperty("java.io.tmpdir")) try { if (tempDir.exists()) { val os = System.getProperty("os.name") val extension = if (os.startsWith("Windows")) ".dll" else if (os.startsWith("Mac OS")) ".dylib" else ".so" if (tempDir.canWrite()) { val file = File(tempDir, "libevo$extension") file.delete() file.createNewFile() try { Library::class.java.classLoader.getResourceAsStream("libevo$extension").use { inputStream -> FileOutputStream(file.absolutePath).use { outputStream -> val buffer = ByteArray(4096) if (inputStream != null) { while (true) { val length = inputStream.read(buffer) if (length == -1) { break } outputStream.write(buffer, 0, length) } } } } } catch (ignored: Exception) { } System.load(file.absolutePath) } else { println("Cannot write to the temp directory.") } } else { println("Cannot find the temp directory.") } } catch (e: Exception) { e.printStackTrace() } } @JvmStatic fun main(args: Array<String>) { val test = create_main_worker(); } }
注意事项
- 如果不需要同步等待结果,可在Rust侧将异步任务提交到运行时后立刻返回任务标识,后续通过回调或者轮询接口获取结果,避免阻塞Java调用线程
- 长耗时异步任务不要在Java主线程调用,避免界面卡顿或者ANR问题
- JNI函数名要严格遵循命名规范,方法名中的下划线需要转义为
_1,和示例中的create_1main_1worker一致
内容的提问来源于stack exchange,提问作者Carter Davis
相关产品推荐
相关产品推荐

