Java通过JNI实现调用C++动态链接库(.so)

@[TOC](Java通过JNI实现调用C++动态链接库(.so /.dll))

Java通过JNI实现调用C++动态链接库(.so)

最近需要将一个Ubuntu系统上搭建的C++项目转到Java上面实现,由于原来的项目比较复杂,并且需要调用一些第三库(Ceres,Eigen),因此直接用Java语言实现的话,很多第三方库无法替代。因此,考虑通过JNI调用C++动态链接库的方式实现Java调用c++函数。 在这里申明一个新定义的函数作为主函数,在原来cpp里面去调用其他的函数,这样编写Java比较方便,同时对原cpp文件改动较少。其实也可以重新建一个cpp文件,如何调用原来项目cpp文件里面定义的函数,这样的好处是不改动源代码。

方法思路:Java+cpp内部定义新函数**

1、Java文件申明一个native函数,目的是在cpp里面调用原函数;
2、Java里面加载动态链接库(.so)入口;

public class INSVLP {
   static {
      System.loadLibrary("insvlpcc"); // Load native library at runtime
   }
   public native void PosCal(String filePath);
   public static void main(String[] args) {
         String cd= System.getProperty("user.dir");  
         String path0=cd+"/config/simu.yaml";
         INSVLP Insvlp=new INSVLP();
         Insvlp.PosCal(path0);  // invoke the native method
   }
}

3、通过JNI工具生成 .h 文件;

javac  INSVLP.java
javac -h .  INSVLP.java

4、根据 .h 文件编写新定义函数 ** JNICALL Java_INSVLP_PosCal **

JNIEXPORT void JNICALL Java_INSVLP_PosCal
  (JNIEnv *, jobject, jstring);//.h文件里面函数的形式
extern "C"  
JNIEXPORT void JNICALL Java_INSVLP_PosCal(JNIEnv *env, jobject obj, jstring filePath) {  }.cpp文件里面函数的形式,传入参数

//注意需要在 .cpp 添加头文件

#include <jni.h>//在JNI文件夹里面
#include "INSVLP.h"

5、生成动态链接库(.so)
生成动态链接库是最关键的一步,涉及到连接外部依赖项、内部函数,指定链接项非常关键,后续的很多报错都是由于没有完整指定链接项造成的,这里也给出一个简便方法。
其实我们在生成动态链接库.so文件和cmake创建项目要用到的依赖库都是相同的。
在cmake项目里面的 /build/CMakeFiles/XXX.dir/link.txt 文件里面存放了完整的依赖项,直接按顺序全部搬过来就行。
在这里插入图片描述
在这里插入图片描述
生成.so文件的指令,可以看见完全一样,这也是 本文的意义所在

g++ -shared -fPIC INSVLP.cc ./src/fileio/fileloader.cc ./src/fileio/filesaver.cc ./src/preintegration/preintegration_base.cc ./src/preintegration/preintegration_earth.cc ./src/preintegration/preintegration_earth_odo.cc ./src/preintegration/preintegration_normal.cc ./src/preintegration/preintegration_odo.cc -o libinsvlpcc.so -lyaml-cpp -labsl_strings -labsl_time -lceres -labsl_str_format_internal -labsl_strings -labsl_strings_internal -labsl_string_view -labsl_throw_delegate -labsl_base -labsl_spinlock_wait -lrt -labsl_int128 -labsl_raw_logging_internal -labsl_log_severity -labsl_civil_time -labsl_time_zone -lglog -lgflags -lspqr -lcholmod -lccolamd -lcamd -lcolamd -lamd -llapack -lf77blas -latlas -lsuitesparseconfig -lrt -lcxsparse -llapack -lf77blas -latlas -lsuitesparseconfig -lrt -lcxsparse -lpthread

踩坑经历

错误一:链接外部库失败

问题描述:出现一些FLAGS ceres等,是因为链接外部库不成功,有可能是安装问题,重新安装,最有可能是**生成动态链接库(.so/.dll)**的时候没有指向对应的库:如 == -lgflags== == -lceres==

undefined symbol: _ZN3fLI7FLAGS_vE;
Exception in thread “main”== java.lang.UnsatisfiedLinkError==: /home/dawn/javafile/ob_gins/libinsvlpcc.so: /home/dawn/javafile/ob_gins/libinsvlpcc.so: undefined symbol: _ZNK5ceres21LocalParameterization18MultiplyByJacobianEPKdiS2_Pd;

错误二:链接内部库(.cpp)失败

问题描述:出现一些FileLoader项目自定义的函数报错undefined symbol,是因为没有指向对应的==.cpp==文件: ./src/fileio/fileloader.cc
undefined symbol: _ZN10FileLoader4openERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEii;

错误三: 32位编译的库不兼容

错误消息中提到的R_X86_64_PC32是一个针对32位相对地址的重定位类型,在x86_64架构中,当创建共享对象时,这种重定位类型通常是不被允许的,因为共享对象在被加载到不同地址空间的进程时,地址必须是可重定位的。
解决方法:修改CMakeLists.txt文件 add_compile_options(-fPIC)
请参考:cmake时添加-fPIC编译选项
/usr/bin/ld: /usr/local/lib/libabsl_raw_logging_internal.a(raw_logging.cc.o): relocation R_X86_64_PC32 against symbol `_ZN4absl16raw_log_internal21internal_log_functionB5cxx11E’ can not be used when making a shared object; recompile with -fPIC

总结

整个过程其实比较简单,主要解释会出现上述的三个错误比较难解决,特别是 undefined symbol,按照**生成动态链接库(.so/.dll)**基本上可以解决上述问题。

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值