目录
一、环境准备
1、安装gcc/g++
2、下载jdk库并配置运行环境
二、配合Java程序创建C++程序的动态库
1、生成要求清单
2、交给C++ 去实现
(1) 接口函数实现
(2) 创建动态库
(3) 检查动态库是否正常链接
3、测试:Java程序调用C++动态库
一、环境准备
既然是同时需要用到 java 和 C/C++,java 的运行环境和编译C++ 的编译器自然就需要事先准备好
1、安装gcc/g++
# 安装gcc
sudo 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、生成要求清单
假设Java要调用的函数接口如下:
public class SVM {
// Java 告诉C++:你帮我实现下面这个函数
// native关键字不可省略
public native int svm_dll_export(double[] random, String savepath);
}
写好以后,现在我们要把清单转变成C++认识的模样,即.h文件。
生成命令:javac -h ./ SVM.java ( ./ 和 SVM.java之间有一个空格)
注意:只要是一个普通类就可以,不需要加任何注释!不然会报错
2、交给C++ 去实现
(1) 接口函数实现
首先我们需要创建一个 SVM_DLL.cpp 文件,这个文件里放的是,上述接口函数的定义。这里可以把声明和定义分离,也可以放在一起,这里为了方便,就把声明和定义放在一起。
第一步,把刚刚上面生成的 SVM.h 中的内容拷贝到 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 test
clean:
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);
public static void main(String[] args) throws InterruptedException{
// 加载动态库
System.load("/home/linux/Templates/workspace/svm/libsvm.so");
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);
}
}
测试结果: