记一次jna调研成果,动机是公司开始做一个公安交通方面的项目,对接华为以及大华时,他们提供的sdk基本都是函数库,所以才有了这次“轰轰烈烈”的调研。
因为目前这方面的资料比较少,所以在调研过程中还是踩了不少坑,将这次成果记录下来,有需要的人可以借鉴一下,基本能满足需要
jna简述
JNA全称Java Native Access,是一个建立在经典的JNI技术之上的Java开源框架。
JNA提供工具用于调用c/c++动态函数库(如Window的dll以及linux的so)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标函数库的函数与结构,JNA将自动实现Java接口方法到函数的映射
jna&c++数据类型对照
java类型 | c++类型 | 原生表现 |
---|---|---|
boolean | int | 32位整数(可定制) |
byte | char | 8位整数 |
char | wchar_t | 平台依赖 |
short | short | 16位整数 |
int | int | 32位整数(可定制) |
long | ong long, __int64 | 64位整数 |
float | float | 32位浮点数 |
double | double | 64位浮点数 |
Buffer/Pointer | pointer | 平台依赖(32或64位指针) |
[] (基本类型的数组) | pointer/array | 2或64位指针(参数/返回值) 邻接内存(结构体成员) |
String | char* | 0结束的数组 (native encoding or jna.encoding) |
WString | wchar_t* | /0结束的数组(unicode) |
String[] | char** | /0结束的数组的数组 |
WString[] | wchar_t** | /0结束的宽字符数组的数组 |
Structure | struct*/struct | 指向结构体的指针(参数或返回值) (或者明确指定是结构体指针)结构体(结构体的成员) (或者明确指定是结构体) |
Union | union | 等同于结构体 |
Structure[] | struct[] | 结构体的数组,邻接内存 |
Callback | (*fp)() | Java函数指针或原生函数指针 |
NativeMapped | varies | 依赖于定义 |
NativeLong | long | 平台依赖(32或64位整数) |
PointerType | pointer | 和Pointer相同 |
使用示例
环境说明:
- jna-version:5.2.0
- jdk-version:1.8
- c++version:c++11
- java开发使用idea
- c++开发使用clion
- g++版本自选
- 下面是jna的maven坐标,也可以将jar下载到本地使用
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.2.0</version>
</dependency>
1、简单调用
实现步骤
- 创建一个接口TestNative继承Library
- 创建一个本地方法(用native修饰的方法)cppHello()
- 创建头文件 library.h,定义一个函数cppHello();
- 创建cpp文件 library.cpp,实现头文件中的cppHello();
- 执行命令生成liblibrary.so动态库
g++ library.cpp -fPIC -shared -o liblibrary.so
- 接口内用Native.load()方法加载函数库(使用绝对路径)创建一个TestNative实例
- TestNative.testNative.cppHello();进行调用
java代码
import com.sun.jna.*;
import java.util.Arrays;
import java.util.List;
/**
* @Author :feiyang
* @Date :Created in 11:14 AM 2019/10/14
*/
public interface TestNative extends Library {
TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
/**
*
* @author feiyang
* @return void
* @date 2019/10/22
* @throws
*/
void cppHello();
/**
* 测试方法
* @param args
*/
static void main(String[] args) {
TestNative.testNative.cppHello();
}
}
c++代码
头文件 library.h
#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H
#include <iostream>
extern "C" {
using namespace std;
void cppHello();
};
#endif
cpp文件 library.cpp
#include "library.h"
#include <iostream>
extern "C" {
void cppHello() {
using namespace std;
cout << "cpp执行:cppHello!" << endl;
};
};
2、基本传参
实现步骤
- 同上
java代码
import com.sun.jna.*;
import java.util.Arrays;
import java.util.List;
/**
* @Author :feiyang
* @Date :Created in 11:14 AM 2019/10/14
*/
public interface TestNative extends Library {
TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
/**
*
* @author feiyang
* @param basic
* @param message
* @return void
* @date 2019/10/22
* @throws
*/
void cppHello(int basic, String message);
/**
* 测试方法
* @param args
*/
static void main(String[] args) {
int basic = 1;
String message = "我是java方,呼叫cpp";
TestNative.testNative.cppHello(basic, message);
}
}
c++代码
头文件 library.h
#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H
#include <iostream>
extern "C" {
using namespace std;
void cppHello(int basic, char * message);
};
#endif
cpp文件 library.cpp
#include "library.h"
#include <iostream>
extern "C" {
void cppHello(int basic, char * message) {
using namespace std;
cout << "cpp收到:" << basic << endl;
cout << "cpp收到:" << message << endl;
};
};
3、模拟结构体传参
实现步骤
- 同上
- 创建一个类TestStruct继承Structure,设置两个String类型的成员变量
filedOne、filedTwo;- 创建两个内部类ByReference、ByValue,继承TestStruct,分别实现接口Structure.ByReference、Structure.ByValue
- 覆盖getFieldOrder()方法
- 头文件定义结构体,方法定义增加形参
- cpp文件实现这个方法
结构体传参分为值传递、引用传递(指针传递)
- 值传递使用ByValue
- 引用传递使用ByReference
值传递
java代码
import com.sun.jna.*;
import java.util.Arrays;
import java.util.List;
/**
* @Author :feiyang
* @Date :Created in 11:14 AM 2019/10/14
*/
public interface TestNative extends Library {
TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
/**
* 本地方法
* @author feiyang
* @param testValue
* @return void
* @date 2019/10/22
* @throws
*/
void cppHello(TestStruct.ByValue testValue);
class TestStruct extends Structure {
public String filedOne;
public String filedTwo;
public static class ByReference extends TestStruct implements Structure.ByReference {
}
public static class ByValue extends TestStruct implements Structure.ByValue {
}
@Override
public String toString() {
return "TestStruct{" +
"filedOne='" + filedOne + '\'' +
", filedTwo='" + filedTwo + '\'' +
'}';
}
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[] { "filedOne", "filedTwo"});
}
}
/**
* 测试方法
* @param args
*/
static void main(String[] args) {
TestStruct.ByValue testValue = new TestStruct.ByValue();
testValue.filedOne = new String("11111");
testValue.filedTwo = new String("22222");
TestNative.testNative.cppHello(testValue);
System.out.println(testValue);
}
}
c++代码
头文件 library.h
#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H
#include <iostream>
extern "C" {
using namespace std;
struct TestStruct {
char *filedOne;
char *filedTwo;
};
void cppHello(TestStruct testStruct);
};
#endif
cpp文件 library.cpp
#include "library.h"
#include <iostream>
extern "C" {
void cppHello(TestStruct testStruct) {
using namespace std;
cout << "value:filedOne:" << testStruct.filedOne << endl;
cout << "value:filedTwo:" << testStruct.filedTwo << endl;
testStruct.filedOne = "update";
testStruct.filedTwo = "update";
};
};
引用传递
java代码
import com.sun.jna.*;
import java.util.Arrays;
import java.util.List;
/**
* @Author :feiyang
* @Date :Created in 11:14 AM 2019/10/14
*/
public interface TestNative extends Library {
TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
/**
* 本地方法
* @author feiyang
* @param testReference
* @return void
* @date 2019/10/22
* @throws
*/
void cppHello(TestStruct.ByReference testReference);
class TestStruct extends Structure {
public String filedOne;
public String filedTwo;
public static class ByReference extends TestStruct implements Structure.ByReference {
}
public static class ByValue extends TestStruct implements Structure.ByValue {
}
@Override
public String toString() {
return "TestStruct{" +
"filedOne='" + filedOne + '\'' +
", filedTwo='" + filedTwo + '\'' +
'}';
}
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[] { "filedOne", "filedTwo"});
}
}
/**
* 测试方法
* @param args
*/
static void main(String[] args) {
TestStruct.ByReference testReference = new TestStruct.ByReference();
testReference.filedOne = new String("11111");
testReference.filedTwo = new String("22222");
TestNative.testNative.cppHello(testReference);
System.out.println(testReference);
}
}
c++代码
头文件 library.h
#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H
#include <iostream>
extern "C" {
using namespace std;
struct TestStruct {
char *filedOne;
char *filedTwo;
};
void cppHello(TestStruct *testStruct);
};
#endif
cpp文件 library.cpp
#include "library.h"
#include <iostream>
extern "C" {
void cppHello(TestStruct *testStruct) {
using namespace std;
cout << "reference:filedOne:" << testStruct -> filedOne << endl;
cout << "reference:filedTwo:" << testStruct -> filedTwo << endl;
testStruct1 -> filedOne = "update";
testStruct1 -> filedTwo = "update";
};
};
注意点
- 值传递时,当cpp代码中对结构体的成员变量进行修改时不会影响到java中的模拟结构体,因为值传递只是拷贝值进行传递
- 引用传递时,当cpp代码中对结构体的成员变量进行修改时是会影响到java中的模拟结构体的,因为引用传递是进行引用地址的传递
回调java方法
实现步骤
- 同上
- 创建一个接口CallBack继承Callback
- 在接口内创建一个方法void invoke();
- 创建一个类实现CallBack,实现invoke方法
- 在本地cppHello()方法设置一个CallBack的形参,将回调函数通过类进行传递
- 在头文件中定义回调函数,并设置cppHello()的函数形参
- 在cpp文件实现cppHello方法
java代码
import com.sun.jna.*;
import java.util.Arrays;
import java.util.List;
/**
* @Author :feiyang
* @Date :Created in 11:14 AM 2019/10/14
*/
public interface TestNative extends Library {
TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
/**
* 本地方法
* @author feiyang
* @param callBack
* @return void
* @date 2019/10/22
* @throws
*/
void cppHello(CallBack callBack);
interface CallBack extends Callback {
void invoke();
}
class CallBackImpl implements CallBack {
@Override
public void invoke() {
System.out.println("回调成功");
}
}
/**
* 测试方法
* @param args
*/
static void main(String[] args) {
TestNative.testNative.cppHello(new CallBackImpl());
}
}
c++代码
头文件 library.h
#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H
#include <iostream>
extern "C" {
using namespace std;
typedef void(*javaCallBack)();
void cppHello(javaCallBack javacallback);
};
#endif
cpp文件 library.cpp
#include "library.h"
#include <iostream>
extern "C" {
void cppHello(javaCallBack javacallback) {
using namespace std;
javacallback();
};
};