Android Native Activity中能否加载含JNI的动态库?
针对Android Native Activity加载JNI依赖动态库的解决方案
一、反射调用System.loadLibrary()的实现方式
完全可以通过Native层反射调用Java的System.loadLibrary(),步骤如下:
- 从
ANativeActivity结构体中获取JavaVM实例,再附加当前线程得到JNIEnv:JavaVM* vm = activity->vm; JNIEnv* env = nullptr; vm->AttachCurrentThread(&env, nullptr); - 反射调用核心代码:
jclass system_cls = env->FindClass("java/lang/System"); if (!system_cls) { // 处理类查找失败 vm->DetachCurrentThread(); return; } jmethodID load_lib_mid = env->GetStaticMethodID(system_cls, "loadLibrary", "(Ljava/lang/String;)V"); if (!load_lib_mid) { env->DeleteLocalRef(system_cls); vm->DetachCurrentThread(); return; } jstring lib_name = env->NewStringUTF("your-library-name"); // 注意不带lib前缀和.so后缀 env->CallStaticVoidMethod(system_cls, load_lib_mid, lib_name); // 检查并处理JNI异常 if (env->ExceptionCheck()) { env->ExceptionClear(); // 加载失败处理逻辑 } env->DeleteLocalRef(lib_name); env->DeleteLocalRef(system_cls); vm->DetachCurrentThread(); - 注意:这种方式会触发目标库的
JNI_OnLoad方法,和Java层直接调用效果一致,且无需在Java层编写任何代码,符合你避免Java层暴露逻辑的需求。
二、修复dlopen()的问题(更高效的原生方案)
dlopen表现不佳通常是参数或使用方式错误,调整后可以正常工作:
- 使用正确的加载参数:
用RTLD_NOW | RTLD_GLOBAL作为flags,确保符号立即解析且全局可见,解决依赖库的符号引用问题:void* handle = dlopen("/data/data/com.your.package/lib/libyour-library.so", RTLD_NOW | RTLD_GLOBAL); if (!handle) { const char* err = dlerror(); // 根据错误信息排查(如路径错误、依赖缺失) return; } - 控制依赖加载顺序:如果你的库依赖其他JNI库,必须先加载依赖库,再加载主库。比如先dlopen依赖的
libfoo.so,再加载你的业务库。 - 绝对路径优先:自定义库尽量使用绝对路径加载,避免系统查找路径的不确定性,路径可以通过
ANativeActivity的internalDataPath或externalDataPath拼接得到。
三、其他可行方案
- 利用构建脚本自动处理依赖:在CMakeLists.txt或Android.mk中指定库的依赖关系,比如CMake中使用
target_link_libraries(your-main-native-lib PRIVATE your-dep-lib),这样系统在加载主库时会自动加载依赖库,无需手动调用dlopen或反射。 - 在主Native库的
JNI_OnLoad中加载依赖:如果你的Native Activity主库是libmain.so,可以在它的JNI_OnLoad方法中完成其他依赖库的加载,确保初始化顺序正确。
内容的提问来源于stack exchange,提问作者OverseerCouncil
相关产品推荐
相关产品推荐

