diff --git a/src/android/fd_bridge.rs b/src/android/fd_bridge.rs index 8d3ed99..8a85473 100644 --- a/src/android/fd_bridge.rs +++ b/src/android/fd_bridge.rs @@ -77,20 +77,21 @@ fn map_exception(env: &mut JNIEnv, fallback: &str) -> Result<(), Error> { } #[cfg(target_os = "android")] -fn cache(env: &mut JNIEnv) -> Result<&'static FdJniCache, Error> { - if let Some(c) = CACHE.get() { - return Ok(c); - } - let class = env - .find_class("app/tauri/serialplugin/UsbNative") - .map_err(|e| Error::new(e.to_string()))?; - let global = env - .new_global_ref(class) - .map_err(|e| Error::new(e.to_string()))?; - let _ = CACHE.set(FdJniCache { class: global }); +fn cache(_env: &mut JNIEnv) -> Result<&'static FdJniCache, Error> { CACHE.get().ok_or_else(not_init) } +/// Called from `UsbNative.nativeInit` on a Java thread, where the app class loader is in scope. +#[cfg(target_os = "android")] +pub fn init_class(env: &mut JNIEnv, class: &JObject) { + if CACHE.get().is_some() { + return; + } + if let Ok(global) = env.new_global_ref(class) { + let _ = CACHE.set(FdJniCache { class: global }); + } +} + #[cfg(target_os = "android")] pub fn init_java_vm(vm: JavaVM) { let _ = JVM.set(vm); diff --git a/src/android/jni.rs b/src/android/jni.rs index 383c761..3dcc369 100644 --- a/src/android/jni.rs +++ b/src/android/jni.rs @@ -32,9 +32,12 @@ pub extern "system" fn Java_app_tauri_serialplugin_MobileBridge_onDeviceDetached #[cfg(target_os = "android")] #[no_mangle] pub extern "system" fn Java_app_tauri_serialplugin_UsbNative_nativeInit( - env: JNIEnv, - _class: JClass, + mut env: JNIEnv, + class: JClass, ) { + // `class` is UsbNative itself. Cache it here: FindClass on a Rust-attached + // thread uses the system class loader and cannot see app classes. + crate::android::fd_bridge::init_class(&mut env, &class); if let Ok(vm) = env.get_java_vm() { crate::android::fd_bridge::init_java_vm(vm); }