Java项目调用C/C++ SDK的方案汇总
- 背景
- 调研
- JNI
- JNative
- JNA
- JavaCPP
背景
Java项目中需要调用到一个C++项目,于是对目前通用的解决方案做了一些调研,这里做一个汇总。
调研
JNI
JNI:Java Native Interface,JNI是一套编程接口,用来实现Java代码与本地的C/C++代码进行交互。
流程:
class文件生成C++头部文件(Test.h)示例,相关命令:
javac -h . MathJniTest.java
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "MathJniTest.h"
/* Header for class MathJniTest *//** Class: MathJniTest* Method: add* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_MathJniTest_add(JNIEnv *, jclass, jint x, jint y) {return x + y;}/** Class: MathJniTest* Method: sub* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_MathJniTest_sub(JNIEnv *, jclass, jint x, jint y) {return x - y;}
int main(int argc, const char* argv[]){}
关于头部文件的C++实现示例:
#include "Test.h"
/* Header for class MathJniTest *//** Class: MathJniTest* Method: add* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_MathJniTest_add(JNIEnv *, jclass, jint x, jint y) {return x + y;}/** Class: MathJniTest* Method: sub* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_MathJniTest_sub(JNIEnv *, jclass, jint x, jint y) {return x - y;}
int main(int argc, const char* argv[]){}
缺点&难点:需要对现有的语音SDK进行修改,增加Java编译后的头部文件及C++的实现,在这部分的代码中调用C++对应函数方法,并重新打包SDK
JNative
JNative:基于JNI的进一步封装
官方文档:http://jnative.free.fr/SPIP-v1-8-3/article.php3?id_article=4
下载第三方包(JNative),引入jar及dll(linux下是.so 动态库文件)
调用对应API实现
CJNative.dll为业务实现的dll文件,sub为方法名称
#include <iostream>int sub (int x, int y) {return x - y;}int main()
{int a = sub(1, 3);std::cout << a;return 0;
}
System.setProperty("jnative.debug", "true");System.setProperty("jnative.loadNative","C:\\Windows\\SysWOW64\\JNativeCpp.dll");JNative n3 = null;try {n3 = new JNative("CJNative.dll", "sub");// 设置返回类型n3.setRetVal(Type.INT);// 设置第一个参数值n3.setParameter(0, 1);n3.setParameter(1,8);n3.invoke();System.out.println("例3:outputString = "+n3.getRetVal());} catch (NativeException | IllegalAccessException e) {e.printStackTrace();}
优点:简单易用
缺点:仅支持windows下的32位系统及linux系统,缺乏完善的文档,并且2006年后不再维护
JNA
JNA:同样是JNI的封装升级
官方文档:https://github.com/java-native-access/jna
同样定义好.h 头部文件
#ifndef JNA_TEST_H
#define JNA_TEST_H#ifdef __cplusplus
extern "C"
{
#endif__declspec(dllexport) int sub(int a, int b);#ifdef __cplusplus
}
#endif
#endif //JNA_TEST_H
Java项目中定义好对应的接口,及加载对应的库(windows下为dll文件)
public interface JNAInterface extends Library {JNAInterface INSTANCE = Native.load("D:\\test\\jna\\Jna.dll*",JNAInterface.class);int sub(int a,int b);
}
执行
public static void main(String[] args) {
// Method[] methods = JNAInterface.INSTANCE.getClass().getDeclaredMethods();
// for (Method method : methods) {
// System.out.println(method.getName());
// }System.out.println(JNAInterface.INSTANCE.sub(1, 3));}
优点:使用简单、文档比较完善且还在持续维护中
JavaCPP
JavaCPP:通过自动生成Java类和本地方法的方式,将C/C++代码包装为Java可调用的接口。
官方文档:https://github.com/bytedeco/javacpp/wiki/Mapping-Recipes
优点:自动生成Java类和本地方法。
缺点:相较于传统JNI,需要修改的业务逻辑 Java 代码会更多,需要考虑内存管理问题。
备注:感觉这个方式还是有一定的上手成本的,而且对于一些C++项目,如果入参和返参是一些很复杂的对象(或指针),转换难度比较大,笔者在实践过程中,碰了很多坑,最终是放弃了。