JNA——Java调⽤CC++动态库
⼯作所需,要使⽤Java调⽤c/c++的动态库,实现Java程序使⽤动态库中的函数。
搜索了⼀番,常⽤的有JNI、JNA⽅法。
JNI(Java Native Interface)
JNI定义了⼀种公⽤的语法,当Java和c/c++都遵循这样的语法时就可以互相调⽤(也可调⽤汇编等其余语⾔)。JNI不能直接调⽤c/c++的库,必须使⽤java编写调⽤函数,⽣成C头⽂件,再利⽤C头⽂件编写C代码,⽣成动态库,最后JNI使⽤新⽣成的动态库完成执⾏。
过程繁琐,需增加改动Java和C/C++的程序。
JNA(Java Native Access)
JNA提供了⼀组Java⼯具类,⽤于在运⾏期间动态访问系统本地库(native library:如Window的dll)⽽不需要编写任何Native/JNI 代码,省去了对c/c++程序的再封装。
最终决定选⽤JNA。
⼀、引⼊
JNA的引⼊很⽅便,使⽤maven直接导⼊即可。
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.1.0</version>
</dependency>
调⽤JNA有很多前提条件
⼆、前提条件
JNA有很多条件限制:
1. JNA只能调⽤C⽅式编译出来的动态库,若是C++代码则需进⾏转换。如何⽤c的⽅式编译c++动态库,可见链接:
2. 使⽤中,Java和c/c++的系统版本必须⼀致,如都是32位或都是64位。
本⽂章全部使⽤64位版本
三、使⽤
1. 引⼊
Jna的样例中,基本都会定义⼀个接⼝,该接⼝链接c/c++动态库,⽣成⼀个实例,并定义了与动态库中⼀致的函数名称,⽤于后续调⽤。举个栗⼦:
/******C端代码*********/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "demo.h"
int hello()
{
printf("Hello world\n");
return 0;
}
将如上代码编译成动态库JnaLibrary.dll
编写Java端调⽤程序:
public interface Clibrary extends Library {
Clibrary instance = (Clibrary) Native.loadLibrary("JnaLibrary.dll", Clibrary.class);
//与动态库中的函数相对应
int hello();
}
/
/调⽤
public class Test {
public static void main(String[] args) {
Clibrary.instance.hello();
}
}
编译执⾏,就会输出动态库的内容:Hello world
2. 数据类型转换
要定义动态库中的函数,难免会涉及⼊参及出参,数据格式的处理就尤为重要。先了解Java与C之间的数据映射。
2.1 基本数据类型
根据对应表来定义即可
C端代码:
......(省略上述代码)
int basicTest(int a, float b)
{
printf("a=%d\n", a);
printf("b=%f\n", b);
return 100;
}
Java端代码:system的头文件
public interface Clibrary extends Library {
Clibrary instance = (Clibrary) Native.loadLibrary("JnaLibrary", Clibrary.class);
int hello();
int basicTest(int a, float b, String pChar);
}
public class Test {
public static void main(String[] args) {
int a=5;
float b = 1.5f;
int ret = Clibrary.instance.basicTest(a,b);
System.out.println("ret:"+ret);
}
}
输出结果:
此处有个疑问:返回结果⽐动态库中函数更早输出了,希望有⼩伙伴能指点
2.2 指针和数组
说到C⾃然会⽤到指针,其转换也花了不少时间。Jna中专门定义了指针类型⽤来与c对应。
在C中指针与数组⼀直暧昧不清,传参时,形参定义⼀个指针,实参则要传⼊地址,⽽传⼀个数组的名称 == 传数组的⾸元素地址,github 中就把两者放在⼀个demo中。
指针及数组:
eg:
c端代码
void arrayTest(char * pStr, unsigned char *puStr)
{
int i = 0;
printf("%s\n", pStr);
for (i = 0; i < 10; i++) {
printf("%c ", puStr[i]);
}
}
Java端:
......(省略其余代码)
/
/定义;其中char *直接使⽤String类型替代,unsigned char * 可转为byte[]数组
void arrayTest(String pStr,  byte[] puStr);
//调⽤
String str = "";
byte[] bytes = new byte[10];
for(int i = 0;i<10;i++){
bytes[i] = (byte)('a'+i);
}
Clibrary.instance.arrayTest(str, bytes);
基本数据类型指针
JNA中给基本数据类型int、float等直接提供了指针类
C端代码:
......(省略上述代码)
void pointerTest(int * pInt, float * pFloat)
{
*pInt = 10;
*pFloat = 12.34;
}
Java端代码:
定义:
//定义
.
.....(省略上述代码)
void pointerTest(IntByReference pInt, FloatByReference pFloat);
//调⽤
......(省略上述代码)
IntByReference pInt = new IntByReference();
FloatByReference pFloat = new FloatByReference();
Clibrary.instance.pointerTest(pInt, pFloat);
System.out.println("out pInt:"+Value());
System.out.println("out pFloat:"+Value());
输出:
指向动态内存的指针
c中可以动态申请空间和赋值,Java使⽤Memory类与之相对应
c端代码:
void mallocTest(char *pszStr)
{
strcpy(pszStr, "Happay Children's Day!");
}
/
/调⽤
int main()
{
char  *pStr = malloc(sizeof(char)*32);
mallocTest(pStr);
free(pStr);
return 0;
}
Java端代码:
//定义
void mallocTest(Memory pString);
/
/调⽤
Memory memory = new Memory(20);
Clibrary.instance.mallocTest(memory);
System.out.println("memory:"+String(0));
输出:
⼆级指针
Jna直接提供了PointerByReference类,有getValue()⽅法。
注意⼆级指针概念,其意是:指向指针的指针,因此Value()获取到的仍是⼀个指针类Pointer,再从该Pointer 中获取值。
eg:
C端代码:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。