使用JNI从Kotlin调用C函数及SWIG编译arm64架构报错问题
问题解决方案
一、修改原生代码支持Kotlin JNI调用
Kotlin的JNI调用逻辑和Java完全一致,原生函数不能直接暴露原有C接口,必须符合JNI调用规范,两种实现方式可选:
- 手动编写JNI适配层
- 先在Kotlin侧声明对应external方法,配置库加载逻辑,示例:
class FeatureCompute { companion object { init { System.loadLibrary("compute_feature_set") } } // 包名、类名、方法名必须和后续JNI函数签名严格对应 external fun computeFeatureSet( input: DoubleArray, fData: DoubleArray, fSize: IntArray, dData: DoubleArray, dSize: IntArray, deData: DoubleArray, deSize: IntArray, v: DoubleArray, mp: DoubleArray, r: DoubleArray, fM: DoubleArray ) external fun terminateCompute() }- 用JDK自带的
javac -h命令(旧版本用javah)根据Kotlin编译出的class文件生成JNI头文件,生成的函数固定格式为Java_<包名下划线分隔>_<类名>_<方法名>,每个函数前两个参数固定为JNIEnv* env(JNI环境指针)、jobject thiz(实例方法对应调用对象,静态方法替换为jclass clazz),后续参数为Kotlin类型映射的JNI类型。 - 实现生成的JNI函数:通过JNI提供的
GetDoubleArrayElements/GetIntArrayElements接口,把Kotlin传入的数组参数转为原生层可操作的double/int指针,校验数组长度符合业务要求(比如input长度必须为1102、v长度必须为7)后,调用你原有的compute_feature_set业务函数;调用完成后通过ReleaseDoubleArrayElements/ReleaseIntArrayElements释放资源,把结果写回数组供Kotlin侧读取。 - 所有暴露给JNI的函数声明必须包裹在
extern "C"块中,避免C++编译时符号名改编导致找不到函数。
- 用SWIG自动生成适配层
不需要手动写JNI桥接代码,只要编写正确的SWIG接口文件,SWIG会自动生成Java/Kotlin侧的调用代码和原生层的桥接逻辑,直接调用你原有C函数即可。
二、SWIG链接阶段Undefined symbols for architecture arm64报错修复
你遇到的报错信息:
Undefined symbols for architecture arm64:
该报错是链接阶段无法找到对应符号实现,按以下优先级排查:
- 解决C/C符号名不匹配问题
你编译C源码用的是gcc(按C规则编译生成符号),链接用的是g(按C++规则查找符号),两边符号修饰规则不一致就会报找不到符号。解决方式:
- 要么统一用gcc完成编译和链接流程;
- 要么在你原有C函数的头文件声明外层加上
extern "C"包裹,强制C++编译器按C规则查找符号:#ifdef __cplusplus extern "C" { #endif // 原有两个函数声明放在这里 extern void compute_feature_set(double input[1102], double F_data[], int F_size[2], double D_data[], int D_size[3], double De_data[], int De_size[4], double V[7], double *mp, double r[12], double *fM); extern void compute_feature_set_terminate(void); #ifdef __cplusplus } #endif
- 检查SWIG接口文件(.i)配置
如果.i文件没有正确声明要导出的函数,SWIG生成的wrap代码会引用不存在的符号,链接必然失败。最小可用的.i文件示例:
%module compute_feature_set %{ // 给wrap编译阶段用,引入你原生函数的声明 #include "compute_feature_set.h" %} // 给SWIG解析用,列出所有需要暴露给上层调用的函数 extern void compute_feature_set(double input[1102], double F_data[], int F_size[2], double D_data[], int D_size[3], double De_data[], int De_size[4], double V[7], double *mp, double r[12], double *fM); extern void compute_feature_set_terminate(void);
- 校验编译目标架构一致性
在arm64架构的Mac设备上,执行lipo -info *.o检查所有生成的目标文件架构,如果存在x86_64架构的文件,重新编译时加上-arch arm64参数指定目标架构。 - 注意平台库后缀差异
macOS系统下动态库后缀为.dylib而非Linux的.so,链接时建议把输出文件名改为libcompute_feature_set.dylib,避免后续Kotlin侧加载库失败。
内容的提问来源于stack exchange,提问作者FerasAS
相关产品推荐
相关产品推荐

