Files
dht/jni/env.rs
T
2026-03-13 02:15:50 +08:00

72 lines
2.5 KiB
Rust

use jni::{JavaVM, JNIEnv};
use jni::objects::{JObject, JValue};
use std::sync::{Arc, Mutex};
/// 跨线程安全的 Java 回调持有者。
/// 保存 JavaVM 指针和 Java 侧 listener 的全局引用,
/// 在任意 Rust 线程中都可 attach 并回调 Java 方法。
#[derive(Clone)]
pub struct JavaCallback {
vm: Arc<JavaVM>,
/// listener 对象的全局引用(不可跨线程直接用,需通过 vm 先 attach)
listener: Arc<Mutex<jni::objects::GlobalRef>>,
}
impl JavaCallback {
/// 从当前 JNI 调用线程创建,把 listener jobject 升级为全局引用。
pub fn new(env: &mut JNIEnv, listener: &JObject) -> jni::errors::Result<Self> {
let vm = env.get_java_vm()?;
let global_ref = env.new_global_ref(listener)?;
Ok(Self {
vm: Arc::new(vm),
listener: Arc::new(Mutex::new(global_ref)),
})
}
/// 在任意线程中 attach JVM,执行 `f(env, listener)` 后自动 detach。
/// `f` 内可调用 Java 方法;出错时 f 应检查 exception 并返回 Err。
pub fn with_env<F, R>(&self, f: F) -> jni::errors::Result<R>
where
F: FnOnce(&mut JNIEnv, &JObject) -> jni::errors::Result<R>,
{
let mut guard = self.vm.attach_current_thread()?;
let listener_guard = self.listener.lock().unwrap();
let result = f(&mut guard, listener_guard.as_obj());
// 检查并清除 Java 侧遗留的异常,避免后续 JNI 调用受污染
if let Err(ref _e) = result {
if guard.exception_check().unwrap_or(false) {
let _ = guard.exception_clear();
}
}
result
}
/// 调用 Java listener 的方法(用于简单通知)。
pub fn call_void_method(
&self,
method_name: &str,
sig: &str,
args: &[JValue],
) -> jni::errors::Result<()> {
self.with_env(|env, obj| {
env.call_method(obj, method_name, sig, args)?;
Ok(())
})
}
}
/// 安全地执行 JNI 代码块,捕获 Rust panic,避免 JVM 崩溃。
/// 若发生 panic,向 Java 抛出 RuntimeException 后返回 `default`。
#[macro_export]
macro_rules! jni_catch {
($env:expr, $default:expr, $body:expr) => {{
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| $body)) {
Ok(v) => v,
Err(_) => {
let _ = $env.throw_new("java/lang/RuntimeException", "Rust internal panic");
$default
}
}
}};
}