JNA调⽤C库简单⽰例-(结构体,回调函数)
⽬录
⼀、JNA简介
1、提到JNA 就不得不提⼀下JNI(Java Native Interface),有过不同语⾔间通信开发经历的⼀般都知道,它允许java和其他语⾔代码(尤其是C/C++)进⾏交互,只要遵守约定即可。⾸先看下JNA调⽤C/C++过程,注意写程序时⾃下⽽上,调⽤时⾃上⽽下:
可见步骤之多,调⽤.dll/.so共享库之痛苦的过程。
若已有编译好的.dll/.so⽂件—>需先⽤是C语⾔另外写⼀个.dll/.so共享库,使⽤SUN规定的数据结构代替C语⾔的数据结构,调⽤已有的dll/so中公布的函数—>java中载⼊这个库—>java编写Native函数作为链接库中函数的代理
问题是很少有java程序员愿意编写调⽤.dll/.so库中原⽣函数的java程序,这也使java在客户端上乏善可⾔,是JNI的⼀⼤弱点!
但是JNA不能完全替代JNI,JNI不仅可以实现java访问C,也可实现C调⽤java。
⽽JNA只能实现Java访问C函数,作为⼀个Java框架,⾃然不能实现C语⾔调⽤Java代码。此时,你还是需要使⽤JNI技术。
那什么是JNA呢?
JNA(Java Native Access)框架是⼀个开源的Java框架,是SUN公司主导开发的,建⽴在经典的JNI的基础之上的⼀个框架。
JNA框架就是为了解决上述JNI弱点⽽开发的,它提供⼀组java⼯具类⽤于在运⾏期间动态访问系统本
地共享类库,java开发⼈员只要在⼀个java接⼝中描述⽬标native library的函数与结构,JNA将⾃动实现Java接⼝到native function的映射,⽽不需要编写任何Native/JNI代码,⼤⼤降低了Java调⽤本体共享库的开发难度。
之所以说它是JNI的替代者,是因为JNA⼤⼤简化了调⽤本地⽅法的过程,使⽤很⽅便,基本上不需要脱离Java环境就可以完成。JNA调⽤C/C++的过程⼤致如下:
可以看到步骤减少了很多,最重要的是我们不需要重写我们的动态链接库⽂件,⽽是有直接调⽤的API,⼤⼤简化了我们的⼯作量。
2、JNA使⽤⼀个⼩型的JNI库插桩程序来动态调⽤本地代码。开发者使⽤java接⼝描述⽬标本地库的
功能和结构,这使得它很容易利⽤本机平台的功能,⽽不会产⽣多平台配置和⽣成JNI代码的⾼开销。这样的性能、准确性和易⽤性显然受到很⼤的重视。此外JNA包括⼀个已与许多本地函数映射的平台库,以及⼀组简化本地访问的共⽤接⼝。
注意:
JNA是建⽴在JNI技术基础之上的⼀个java类库,它提供了⼀个动态的C语⾔编写的转发器,可以⾃动实现java和C的数据类型映射,不需要再编写C动态链接库,可⽅便使⽤java直接访问动态链接库中的函数,JNA性能上有些微损失。
⼆、Demo⽰例
该项⽬是⼀个简单的Springboot项⽬,需要引⼊的jar包:
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.5.1</version>
</dependency>
三、Demo详细说明
1、⾸先提供⼀个半段项⽬运⾏环境是windos 还是 linux的⼯具类
import com.sun.jna.Platform;
slf4j.Slf4j;
@Slf4j
public class BaseFun {
/**
* 获取操作平台信息
* @return
*/
public static String getOsArch() {
String arch = Property("os.arch").toLowerCase();
final String name = Property("os.name");
String osArch;
OSType()) {
case Platform.WINDOWS: {
if ("i386".equals(arch))
arch = "x86";
else if ("x86_64".equals(arch)) {
arch = "amd64";
}
osArch = "win32-" + arch;
}
break;
default: {
osArch = LowerCase();
if ("x86".equals(arch)) {
arch = "i386";
}
if ("x86_64".equals(arch)) {
arch = "amd64";
}
int space = osArch.indexOf(" ");
if (space != -1) {
osArch = osArch.substring(0, space);
}
osArch += "-" + arch;
}
break;
}
return osArch;
}
public static String LoadSDKLibrary() {
String filePath = Property("user.dir").replaceFirst("/","").replaceAll("%20"," ");
//String filePath = ClassLoader().getResource("").getPath().substring(1);  String loadLibrary = "";
String OsArch = getOsArch();
System.out.println("OsArch=" + OsArch);
System.out.println("filePath=" + filePath);
LowerCase().startsWith("win32-x86")) {
LowerCase().startsWith("win32-x86")) {
loadLibrary = filePath + "\\libs\\test\\win64\\testClientDll";
} else LowerCase().startsWith("win32-amd64") ) {
loadLibrary = filePath + "\\libs\\test\\win64\\testClientDll";
}else {
//linux
loadLibrary = "test_client_api.so";
}
log.info("[Load Library Path : {}]", loadLibrary);
return loadLibrary;
}
public static void GetPointerDataToStruct(
Pointer pNativeData, long OffsetOfpNativeData, Structure pJavaStu) {
pJavaStu.write();
Pointer pJavaMem = Pointer();
pJavaMem.write(0, ByteArray(OffsetOfpNativeData, pJavaStu.size()), 0, pJavaStu.size());
}
public static void GetPointerData(Pointer pNativeData, Structure pJavaStu)
{
GetPointerDataToStruct(pNativeData, 0, pJavaStu);
}
public static void GetPointerDataToStructArr(Pointer pNativeData, Structure[]pJavaStuArr) {
long offset = 0;
for (int i=0; i<pJavaStuArr.length; ++i)
{
GetPointerDataToStruct(pNativeData, offset, pJavaStuArr[i]);
offset += pJavaStuArr[i].size();
}
}
public static void SetStructDataToPointer(Structure pJavaStu, Pointer pNativeData, long OffsetOfpNativeData){  pJavaStu.write();
Pointer pJavaMem = Pointer();
pNativeData.write(OffsetOfpNativeData, ByteArray(0, pJavaStu.size()), 0, pJavaStu.size()); }
public static void SetStructArrToPointerData(Structure[]pJavaStuArr, Pointer pNativeData) {
long offset = 0;
for (int i = 0; i < pJavaStuArr.length; ++i) {
SetStructDataToPointer(pJavaStuArr[i], pNativeData, offset);
offset += pJavaStuArr[i].size();
}
}
}
2、编写⼀个TestClientLib接⼝,继承Library接⼝, 该接⼝类主要是关联C库⽂件中的接⼝
⽐如C代码的接⼝是这样的:
/**回调函数**/
typedef void (* YZRC_EVENT_HANDLER)(YZRC_EVENT_TYPE eventType, void *pParam, int paramSize);
int  init_wan_mode(char *pRadarName, char *pRadarAddr, YZRC_EVENT_HANDLER eventHandler);
对应jna要编写的接⼝:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;
import java.util.ArrayList;
import java.util.List;
public interface TestClientLib extends Library {
TestClientLib TEST_CLIENT_LIB = (TestClientLib) Native
.loadLibrary(BaseFun.LoadSDKLibrary(), TestClientLib.class);
public static final int NAME_SIZE= 10;
public static final int LIST_SIZE= 10;
/**
* 结构体定义
*/0
public static class TEST_NAME_INFO extends Structure {
public int count = 0;
public byte[][]  nameList = new byte[LIST_SIZE][NAME_SIZE + 1];
public static class ByReference extends TEST_NAME_INFO implements Structure.ByReference {        }
public static class ByValue extends TEST_NAME_INFO implements Structure.ByValue {
}
public TEST_NAME_INFO() {
super();
this.setAlignType(Structure.ALIGN_DEFAULT);
java jna}
public TEST_NAME_INFO(Pointer pointer) {
super(pointer);
this.setAlignType(Structure.ALIGN_DEFAULT);
}
@Override
protected List<String> getFieldOrder() {
List<String> fieldOrderList = new ArrayList<String>();
fieldOrderList.add("count");
fieldOrderList.add("nameList");
return fieldOrderList;
}
}
/**
*  回调函数
*/
public interface TEST_EVENT_HANDLER extends StdCallCallback {
public void invoke(int eventType, Pointer pointer, int paramSize);
}
/**
*    // pUserName:输⼊,⽤户名
*    // pPassword:输⼊,密码
*    // eventHandler:回调函数
*/

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