Android平台加载liboqs.so后出现Native方法未找到异常的求助
你已经成功加载了liboqs.so,但核心问题在于你编译的liboqs库中没有包含liboqs-java的JNI绑定代码——liboqs本身是纯C密码库,而liboqs-java的c/文件夹里是专门连接Java与liboqs的JNI封装代码,这些代码没被编译到你的liboqs.so中,导致Java调用Native方法时找不到实现。
下面是具体的解决步骤:
1. 确认JNI方法签名与C实现匹配
先验证你的liboqs.so中是否确实缺少对应的JNI方法符号:
- 从
jniLibs/x86_64/中复制出liboqs.so,使用NDK自带的nm工具检查:# 替换成你本地NDK路径下的x86_64 nm工具 $NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android-nm -D liboqs.so | grep max_number_sigs
如果没有输出,说明这个JNI方法确实不在库中,接下来需要把liboqs-java的JNI代码编译进去。
2. 配置NDK编译脚本(推荐用CMakeLists.txt)
你需要创建一个CMake配置文件,将liboqs-java的c/文件夹下所有JNI封装代码编译进去,并链接到liboqs库。
示例CMakeLists.txt配置
将文件放在src/main/cpp/CMakeLists.txt(如果你的JNI代码在src/main/c/,路径要对应调整):
cmake_minimum_required(VERSION 3.10.2) # 1. 引入预编译的liboqs.so add_library(liboqs SHARED IMPORTED) set_target_properties(liboqs PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/liboqs.so # 引入liboqs的头文件,假设你把liboqs的include目录放在src/main/c/include/ INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/src/main/c/include) # 2. 编译liboqs-java的JNI封装代码 add_library(oqs-jni SHARED src/main/c/sigs.c src/main/c/key_exchange.c src/main/c/common.c src/main/c/kem.c src/main/c/sig.c # 把liboqs-java c/文件夹下所有.c文件都列在这里 ) # 3. 链接依赖库 target_link_libraries(oqs-jni liboqs log # Android日志库,JNI中打日志需要 android)
同步build.gradle配置
在app模块的build.gradle中添加CMake支持:
android { ... defaultConfig { ... externalNativeBuild { cmake { abiFilters 'x86_64' // 匹配你的模拟器架构,需其他ABI可自行添加 cppFlags "-std=c99" // liboqs依赖C99标准 } } } externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" // 指向你的CMake配置文件路径 } } // 确保jniLibs和JNI代码目录被正确识别 sourceSets { main { jniLibs.srcDirs = ['src/main/jniLibs'] jni.srcDirs = ['src/main/c'] // 你的JNI代码所在目录 } } }
3. 修改Java中的库加载逻辑
现在编译出的是liboqs-jni.so,需要先加载依赖的liboqs.so,再加载包含JNI绑定的库:
public static void loadNativeLibrary() { System.out.println("Loading liboqs.so for: " + Build.CPU_ABI + "..."); System.loadLibrary("oqs"); System.loadLibrary("oqs-jni"); // 加载包含JNI绑定实现的库 }
如果你修改CMake把JNI代码直接编译进liboqs.so,则只需要保留原来的System.loadLibrary("oqs")即可。
4. 验证编译结果
重新编译项目后,检查app/build/intermediates/cmake/debug/obj/x86_64/下的liboqs-jni.so,再次用nm命令查看是否包含Java_org_openquantumsafe_Sigs_max_1number_1sigs符号,有输出则说明编译成功。
额外检查点
- 确保liboqs-java的JNI代码中的包名完全匹配Java类的包名(
org.openquantumsafe),JNI方法名必须严格遵循Java_包名_类名_方法名格式,原方法名中的下划线要用_1代替。 - 检查JNI方法的参数和返回值与Java中的Native方法完全一致:比如
max_number_sigs()是无参数的int方法,C中的实现必须是JNIEXPORT jint JNICALL Java_org_openquantumsafe_Sigs_max_1number_1sigs(JNIEnv *env, jclass clazz)。 - 确认Android模拟器的ABI与你编译的库ABI完全匹配(x86_64),避免架构不兼容。
内容的提问来源于stack exchange,提问作者Taimoor Ahmad

