最近项目中用到了java调用linux系统下c的so动态库的需求,实现后我就来总结一波
实现方式:
1.java使用jni调用so库:
需要自己定义native方法,编译.h文件,编写c文件,在linux上编译为so文件,巴拉巴拉。。。
总之比较繁琐,而且写java的去写c,你懂得。。。
2.对jni做了封装的JNA方法:
将c中的类型与Java中的类型做了映射,只需要写一个类,类中的接口extends Library来实例化代码,加载c的.so文件就可以了,怎么样,听起来是不是比jni的调用方式简单很多呢?
前期准备,maven依赖
1.要使用JNA当然需要依赖于其jar包了,那么相应的依赖的引入就必不可少了,依赖如下:
net.java.dev.jna
jna
5.2.0
net.java.dev.jna
jna-platform
5.2.0
示例代码
1.so中代码或者是cpp文件中要调用方法的定义:
int ppdetect(char *modelPath,cv::Mat &input_img, int
* column, int gpuid, int *boxX, int *boxY, int *boxWidth, int *boxHeight, int
*numBox);
2.java中封装代码的实例(注意方法名要与c中定义一致,否则会找不到方法):
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import sun.misc.BASE64Decoder;
public class CallSoUtils {
/**
* 继承Library,用于加载库文件
* @author I
*
*/
public interface PPLibrary extends Library {
//linux下代码编译出来的文件实际上是libPPDetectSDK.so文件,但是在代码中需要去掉lib和.so
PPLibrary INSTANTCE = (PPLibrary) Native.loadLibrary("PPDetectSDK", PPLibrary.class);
// 此方法为链接库中的方法
/*
* int ppdetect(char *modelPath,cv::Mat &input_img, int
* column, int gpuid, int *boxX, int *boxY, int *boxWidth, int *boxHeight, int
* *numBox);
*/
int ppdetect(String modelPath,long matImgAddress,int column,int gpuid,int[] boxX,
int[] boxY, int[] boxWidth, int[] boxHeight,IntByReference numBox);
}
/**
* windows下调用add_core_lite.dll文件,可以写全dll文件名,可以是全路径,如果不是全路径需要将dll文件放到jdk
* 的bin目录下
* @author I
*
*/
public interface TestDllInterface extends Library {
TestDllInterface INSTANTCE = (TestDllInterface) Native.loadLibrary("add_core_lite", TestDllInterface.class);
boolean calcvalptr(int[] paramin, int numb_in,int[] pAns, IntByReference numb_out);
}
}
3.java调用封装类的方法实例(mvc中controller代码,有一些删减,关注方法里边的思路,或者自己提取到main方法中 也可以):
@RequestMapping(value = "/ppdec", method = { RequestMethod.POST })
public String paperSplit(@RequestBody final JSONObject parseObject) {
String result = "";
try {
// base64编码图片信息
String base64 = parseObject.getString("base64");
// model 路径
String modelPath = parseObject.getString("modelPath");
Mat matrix = null;
try {
//自定义的实现base64转mat方法
matrix = Base2MatTools.base642Mat(base64);
} catch (IOException e) {
}
int column = 3;
int gpuid = 2
int[] boxX = new int[3];
int[] boxY = new int[3];
int[] boxWidth = new int[3];
int[] boxHeight = new int[3];
IntByReference numBox = new IntByReference(0);
// 使用jna进行算法的调用,mat传递的是地址
int ret = PPLibrary.INSTANTCE.ppdetect(modelPath, matrix.getNativeObjAddr(),
column, gpuid, boxX, boxY, boxWidth, boxHeight, numBox);
List rectList = new ArrayList();
if (numBox != null && numBox.getValue() > 0) {
for (int i = 0; i < numBox.getValue(); i++) {
int x = boxX[i];
int y = boxY[i];
int w = boxWidth[i];
int h = boxHeight[i];
Rect rect = new Rect(x, y, w, h);
rectList.add(rect);
}
}
jsonObject.put("status", "success");
jsonObject.put("code", "200");
jsonObject.put("data", rectList);
return jsonObject.toString();
} catch (Exception e) {
}
return result;
}
一个demo完成了。
可能好多人弄不懂为什么这样写,那么我就将JNA中java和c的对应关系甩到下边供各位参考
这其实只是一个参考,向int& 实际上很可能需要转换的是int[],而不是IntByReference
【注】使用JNA最重要的就是对应类型的转换,要是类型的转换出现错误,分分钟让你jvm崩溃,所以一定要注意哦。
另外,假如传递的是Mat类型的数据注意一定传递的是指针地址,而不是整个Mat类型的数据,要不然一样崩溃给你看啊啊啊啊啊…。