JNI与JNA对比
JNI介绍
最开始,java与sdk相互调用,使用的是JNI技术,操作步骤如图
步骤如下:
(A) 用c/c++将已有的sdk进行重新编写(用sun规定的数据结构替代c/c++中的数据结构)
(B) Java中引入第1步中新编写的sdk
(C) 编写java native函数作为链接函数中的代理
(D) 用java调用代理函数
优点:JNI针对sdk是双向调用。sdk可以调用java的API,java可以调用sdk的API
缺点:流程繁琐复杂,需要根据sun公司定义的通用数据结构将源sdk重写一遍(用c/c++),实在头疼
JNA介绍
正是由于JNI操作sdk太复杂,所以sun公司主导开发了JNA框架,其目的就是为了简化:java调用sdk的步骤
步骤如下:
(A) 编写java native函数作为链接函数中的代理
(B) Java调用代理API
优点:简单,省去了根据sun规定的通用数据结构重写sdk的步骤
缺点:单向调用,只有java调用sdk的API,没有sdk调用java的API
JNA运用
加载
windows平台sdk(xxx.dll)加载方法:
public interface MyLibrary extends Library {
// 不要写扩展名
MylLibrary INSTANCE = Native.load("xxx", MyLibrary.class);
}
linux平台sdk(xxx.so)加载方法:
public interface MyLibrary extends Library {
// 要写扩展名
MylLibrary INSTANCE = Native.load("xxx.so", MyLibrary.class);
}
类型对应
回调函数
sdk代码
void CALLBACK PDCSSTATUS(
int DCSID,
int DeviceStatus,
int DeviceFault,
int *pAmpStatus,
int ContactInputOpenFault,
int ContactInputShortFault
);
注意:这里关注【int *pAmpStatus】,这是一个【int指针】,java中用【IntByReference】来做对应。(c/c++中的所有指针类型,JNA中都有对应的xxxByReference与之对应)
java映射代码
public interface MyLibrary extends Library {
MylLibrary INSTANCE = Native.load("xxx.so", MyLibrary.class);
// 与SDK中的方法对应
interface PDCSSTATUS extends Callback {
void callback(int dcsID, int deviceStatus, int deviceFault,
IntByReference pAmpStatus, int contactInputOpenFault,
int contactInputShortFault);
}
}
java调用
public class MyTest {
// dcs设备信息回调方法
static MyLibrary.PDCSSTATUS pdcsstatus = (dcsID, deviceStatus, deviceFault, pAmpStatus, contactInputOpenFault, contactInputShortFault) -> {
log.info("============================================");
log.info("callback-dcsId:" + dcsID);
log.info("callback-deviceStatus:" + deviceStatus);
log.info("callback-deviceFault:" + Integer.toBinaryString(deviceFault));
// 解析设备状态码
log.error("callback-pAmpStatus:");
int[] intArray = pAmpStatus.getPointer().getIntArray(0, 8);
for (int i = 0; i < intArray.length; i++) {
log.info("通道" + i + ":" + Integer.toBinaryString(intArray[i]));
}
};
}
返回类型为指针,指向数组
sdk代码(其实与上文是同一个函数)
void CALLBACK PDCSSTATUS(
int DCSID,
int DeviceStatus,
int DeviceFault,
int *pAmpStatus,
int ContactInputOpenFault,
int ContactInputShortFault
);
参数说明:
pAmpStatus:
这是一个整数数组,长度为8,对应于8个功放通道的状态和故障。
每个数组元素的8bits描叙一个功放通道的状态和故障。
Bit0: 功放通状态(0:未启用,1:正常运行, 2:未正常运行).
Bit1: 回路故障 (1: 故障, 0: 正常);
Bit2: 功放是否禁用 (1: 禁用, 0: 未禁用);
Bit3: 保留
Bit4: 电源或保护故障(1: 故障, 0: 正常);
Bit5~Bit7: 保留.
注意:
1. 【int *pAmpStatus】,这是一个【int指针】,java中用【IntByReference】来做对应
2. pAmpStatus参数指向的是一个int[]
java映射代码
public interface MyLibrary extends Library {
MylLibrary INSTANCE = Native.load("xxx.so", MyLibrary.class);
// 与SDK中的方法对应
interface PDCSSTATUS extends Callback {
void callback(int dcsID, int deviceStatus, int deviceFault,
IntByReference pAmpStatus, int contactInputOpenFault,
int contactInputShortFault);
}
}
java调用
public class MyTest {
// dcs设备信息回调方法
static MyLibrary.PDCSSTATUS pdcsstatus = (dcsID, deviceStatus, deviceFault, pAmpStatus, contactInputOpenFault, contactInputShortFault) -> {// 解析设备状态码
log.error("callback-pAmpStatus:");
int[] intArray = pAmpStatus.getPointer().getIntArray(0, 8);
for (int i = 0; i < intArray.length; i++) {
log.error("通道" + i + ":" + Integer.toBinaryString(intArray[i]));
}
};
}
处理指针步骤:
1. 得到指针:getPointer()
2. 根据sdk文档,指针指向的是一个int[8],所以getIntArray(0,8)得到对应的数据
3. 核心代码:int[] intArray = pAmpStatus.getPointer().getIntArray(0, 8);
结构体
本次的sdk中无结构体代码,故没有应用,也就没有研究
问题汇总
sdk无法加载
1. sdk根据不同平台,加载方法不同。上文已经给出了windows平台、linux平台的加载方法
2. sdk版本与系统版本不一致。例如:sdk是32位,而系统是64位
3. sdk依赖库的缺失
linux可以用:ldd,命令查看
如图:=>右侧没有出现 not found,表示依赖满足
windows可以用:【dumpbin】进行查看,这里没有做对应的演示
回调函数不执行
java代码定义回调函数时,将其设置为静态变量,防止jvm对其进行回收
结束语:感谢各位的耐心阅读。