OpenXR API Layer动态库中JNI无法从外部AAR查找类
解决OpenXR API Layer中JNI查找AAR类失败的问题
核心问题本质
你碰到的ClassNotFoundException,根源是OpenXR Layer动态库的运行上下文类加载器,无法访问你引入的AAR对应的Dex文件。哪怕build.gradle已经配置依赖、dexdump验证类已加载,但OpenXR Layer作为独立动态库,其类加载器和主APP的类加载器并非完全共享,直接调用FindClass或用Native Activity的类加载器,都覆盖不到AAR的Dex路径。
可行解决方案
1. 主动传递主APP的ClassLoader到OpenXR Layer
既然没法干预JVM初始化流程,最直接的办法是从主APP Java层,把能加载mudraAndroidSDK.model.MudraDevice的ClassLoader传给Layer:
- 在主APP的Activity(比如NativeActivity子类)中获取类加载器:
ClassLoader appClassLoader = getApplicationContext().getClassLoader(); - 通过JNI将这个ClassLoader对象保存为全局引用,或者借助OpenXR扩展机制直接传递给Layer(如果Layer支持自定义参数)。
- 在OpenXR Layer的JNI代码中,用这个ClassLoader加载目标类:
jclass classLoaderCls = env->GetObjectClass(classLoaderObj); jmethodID loadClassMethod = env->GetMethodID(classLoaderCls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); jstring clsName = env->NewStringUTF("mudraAndroidSDK.model.MudraDevice"); jclass targetCls = static_cast<jclass>(env->CallObjectMethod(classLoaderObj, loadClassMethod, clsName)); // 清理局部引用 env->DeleteLocalRef(clsName); env->DeleteLocalRef(classLoaderCls);
2. 强制将AAR类打包到主Dex
如果AAR体积不大,可以把目标类强制放到主Dex里,确保类加载器能直接找到:
- 在build.gradle的android块中配置规则:
android { buildTypes { debug { multiDexKeepProguard file('multiDexKeep.pro') } release { multiDexKeepProguard file('multiDexKeep.pro') } } } - 在同级目录的
multiDexKeep.pro中添加:
这样打包时会把该类强制放入主Dex,避免被拆分到辅助Dex,Native层用-keep class mudraAndroidSDK.model.MudraDevice { *; }FindClass就能直接定位。
3. 确认Layer的加载时机与进程上下文
OpenXR Layer可能在主APP完全初始化前就被加载,此时AAR的Dex还没被系统类加载器加载:
- 在主APP的
onCreate中主动触发AAR类的初始化(比如Class.forName("mudraAndroidSDK.model.MudraDevice")),再启动OpenXR相关逻辑。 - 验证Layer是否在主APP进程运行,打印进程ID确认:
__android_log_print(ANDROID_LOG_DEBUG, "XR_LAYER", "Current PID: %d", getpid());
验证排查手段
- 用
adb shell dumpsys package <你的包名>查看dexPaths列表,确认AAR的Dex是否在其中。 - 在JNI代码中打印当前线程的上下文类加载器,对比主APP的类加载器:
jclass threadCls = env->FindClass("java/lang/Thread"); jmethodID getLoaderMethod = env->GetMethodID(threadCls, "getContextClassLoader", "()Ljava/lang/ClassLoader;"); jobject contextLoader = env->CallObjectMethod(env->GetCurrentThread(), getLoaderMethod); jclass loaderCls = env->FindClass("java/lang/ClassLoader"); jmethodID toStringMethod = env->GetMethodID(loaderCls, "toString", "()Ljava/lang/String;"); jstring loaderStr = static_cast<jstring>(env->CallObjectMethod(contextLoader, toStringMethod)); const char* loaderCStr = env->GetStringUTFChars(loaderStr, nullptr); __android_log_print(ANDROID_LOG_DEBUG, "XR_LAYER", "Current ClassLoader: %s", loaderCStr); // 清理引用 env->ReleaseStringUTFChars(loaderStr, loaderCStr); env->DeleteLocalRef(loaderStr); env->DeleteLocalRef(loaderCls); env->DeleteLocalRef(contextLoader); env->DeleteLocalRef(threadCls);
内容的提问来源于stack exchange,提问作者Naor Liron
相关产品推荐
相关产品推荐

