目录

一、环境准备

1、安装gcc/g++

2、下载jdk库并配置运行环境

二、配合Java程序创建C++程序的动态库

1、生成要求清单

2、交给C++ 去实现

(1) 接口函数实现

(2) 创建动态库

(3) 检查动态库是否正常链接

3、测试:Java程序调用C++动态库


参考文章:Linux环境下,java调用C/C++动态库

一、环境准备

既然是同时需要用到 java 和 C/C++,java 的运行环境和编译C++ 的编译器自然就需要事先准备好

1、安装gcc/g++

# 安装gccsudo apt-get install gcc# 安装g++sudo apt-get install g++

2、下载jdk库并配置运行环境

jdk 是java依赖的软件工具包,运行java程序必不可少的要素。安装流程参考:JDK1.8

注意:记一下jdk的安装路径,下面要用

二、配合Java程序创建C++程序的动态库

Java程序调用的函数接口,是java程序决定的(比如函数名、形参、返回值)。C++只负责去实现,然后打包成动态库提供给Java。关于这个流程,可以这么理解:

  • Java 提供要求清单(接口函数的声明)
  • 交给C++去实现
  • C++实现以后,Java调用C++生成的动态库

1、生成要求清单(Windows环境下操作)

生成要求清单,其实就是C++可以识别的接口函数的声明(.h文件),接口函数的声明会携带项目路径的一部分,为了和Windows下保持一致,我们需要先在Windows环境下生成 .h 文件。

(1) 设计Java接口

假设Java要调用的函数接口如下:

public class SVM {    // Java 告诉C++:你帮我实现下面这个函数    // native关键字不可省略public native int svm_dll_export(double[] random, String savepath);    }

写好以后,下面我们要把清单转变成C++认识的模样,即.h文件。

(2) 转换Java接口

先找到 SVM.java 在本地的存储位置。

打开该路径下的命令行窗口

输入生成命令:javac -h ./ SVM.java ( ./ 和 SVM.java之间有一个空格)

注意:只要是一个普通类就可以,不需要加任何注释!不然会报错

成功以后,可以在SVM.java同目录下看到生成的 .h 文件

2、交给C++ 去实现(Linux环境下操作)

(1) 接口函数实现

首先我们需要创建一个 SVM_DLL.cpp 文件,这个文件里放的是,上述接口函数的定义。这里可以把声明和定义分离,也可以放在一起,这里为了方便,就把声明和定义放在一起

第一步,把刚刚上面生成的 com_example_demo_utils_algorithm_SVM.h 中的内容拷贝到 SVM_DLL.cpp 中。(或者将 com_example_demo_utils_algorithm_SVM.h 上传到Linux平台然后改名为 SVM_DLL.cpp)

第二步,开始实现接口逻辑。jdoubleArray、jstring 是 jni 库提供的类型,是兼容java和C++数据类型的关键。我们要将 jdoubleArray 转换成 C++可以识别的类型。

基本类型的转化可以参考:jni基本类型的转换

jsstring / jdoubleArray类型的转换可以参考:jstring / jdoubleArray类型的转换

(2) 创建动态库

把实现上述代码逻辑时要用到的 .h 文件和 .c 文件放到同一个文件夹下。编写makefile,方便一键生成。

CC=g++              # 如果使用的是C,这里就是gcc;如果是C++,这里就是g++    TARGET=libsvm.so    # 目标动态库文件# 生成动态库所依赖的目标文件TMPTARGET=SVM_DLL.o vacTestAcc.o vacSvmUtiles.o vacSvmClassify.o vacGetSampEn.o   # 生成动态库需要的第三方头文件的搜索路径COND=-std=c++11 \ -I/usr/local/java/jdk1.8.0_351/include/ \          # jdk安装目录下:jni.h(必须要有) -I/usr/local/java/jdk1.8.0_351/include/linux/ \    # jdk安装目录下:jni_md.h(必须有) -I/usr/local/include/eigen-3.3.9/ \              # 逻辑实现所需的第三方库目录(非必须) -fpermissive     #(非必须)$(TARGET):$(TMPTARGET)$(CC) -shared -o $@ $^ $(COND)rm $(TMPTARGET)            # 移除 .o 文件%.o: %.cpp                             # 等价于下面的写法$(CC) -fPIC -c $< $(COND) #SVM_DLL.o:SVM_DLL.cpp                 # 下面的.cpp文件都是逻辑实现要用到的,等价于上面的写法#$(CC) -fPIC -o $(@) -c $^ $(COND)  #vacTestAcc.o:vacTestAcc.cpp#$(CC) -fPIC -o $(@) -c $^ $(COND)#vacGetSampEn.o:vacGetSampEn.cpp#$(CC) -fPIC -o $(@) -c $^ $(COND)#vacSvmClassify.o:vacSvmClassify.cpp#$(CC) -fPIC -o $(@) -c $^ $(COND)#vacSvmUtiles.o:vacSvmUtiles.cpp#$(CC) -fPIC -o $(@) -c $^ $(COND):.PHONY:clean testclean:rm $(TARGET)

在命令行输入:make。之后就会自动打包生成动态库。

(3) 检查动态库是否正常链接

输入 ldd -r 动态库名称 可以检查动态库的链接状况。这里我们输入:ldd -r libsvm.so

正常情况下:

异常情况下:

出现异常情况的原因大致有两种:

a. xx.cpp文件存在报错

至于是哪个文件报错,可以输入“ c++filt符号名 ”来确定是哪里出了问题,这里的符号名指的是_ZN10vacTestAcc16vacTestSampEnAccEPdiPKc

b. 逻辑实现时,某个要用的 .cpp 文件没有被编译生成 .o 文件。此时请检查 .o 文件是否有遗漏

3、Java程序调用C++动态库

我们回到 Java 程序,Java调用动态库里的函数之前,需要先加载动态库。基本流程如下:

  • 加载动态库。System.load(“动态库所在路径”)
  • 调用库函数。
public class SVM {public native int svm_dll_export(double[] random, String savepath);    static{        // 加载动态库        System.load("/home/linux/Templates/workspace/svm/libsvm.so");      }public static void main(String[] args) throws InterruptedException{double[] arr = new double[]{1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0};String savepath = "../model";        // 调用库函数int ret = new SVM().svm_dll_export(arr, savepath);                   System.out.println("C++计算返回的结果是: "+ ret);}}