本文已参与「新人创作礼」活动,一起开启掘金创作之路。
提到物联网开发中用到最多的方式,除了TCP,还得是DLL调用,因为很多第三方PLC不支持二次嵌入开发,只能使用他们给定的调用DLL进行访问通信,这就不得不用到Java中的JNA(JNI)技术了。
1. 什么是JNA
谈到JNA之前,他还有个前辈叫JNI,用于访问本地native方法,是jdk原生自带的组件,而JNA,是建立在JNI基础之上,对JNI的进一步封装,让对DLL的接入开发更佳快捷友好。
2. 开发准备
2.1 maven引入
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.3.1</version>
</dependency>
2.2 引入dll文件
在项目的resources目录下分别建立w32-x86(32位dll库)和w32-x86-64(64位dll库)
3. 开发Library
3.1 JNA数据类型对应关系
| Native Type | Java Type | Native Representation |
|---|---|---|
| char | byte | 8-bit integer |
| wchar_t | char | 16/32-bit character |
| short | short | 16-bit integer |
| int | int | 32-bit integer |
| int | boolean | 32-bit integer (customizable) |
| long, __int64 | long | 64-bit integer |
| long long | long | 64-bit integer |
| float | float | 32-bit FP |
| double | double | 64-bit FP |
| pointer | Buffer/Pointer | |
| char* | String | |
| wchar_t* | WString | |
| char** | String[] | |
| wchar_t** | WString[] | |
| void* | Pointer | |
| void ** | PointerByReference | |
| int& | IntByReference | |
| int* | IntByReference | |
| struct | Structure | |
| (*fp)() | Callback | |
| varies | NativeMapped | |
| long* | NativeLong |
3.2 定义Library接口
要在Java中先定义dll的方法接口,方法名,返回值,参数列表类型必须和dll中提供的方法保持一致
public interface DemoLibrary extends Library {
int sum(int a,int b);
}
3.3 创建对象使用
DemoLibrary demoLibrary = Native.load("DLL_NAME",DemoLibrary.class);
int result=demoLibrary.sum(2,3);
DLL_NAME仅限dll的名称,dll文件放在resources对应的win32目录下,不带dll后缀,不带目录,jna自行寻找加载。
3.4 dll库加载规则
1). Native.load方法里写入dll完整路径
2). 如果是jni开发,则需要指定dll库的完整路径,或者使用
// 设置path路径,需要先查询当前的path,然后追加在后面,不建议直接set
// 追加后的效果相当于在系统变量里添加了path值
System.setProperty("java.library.path", your_library_dir);
3). 只输入DLL库名称,则会在下列目录中查找,找不到则报错:
| 目录 | 使用环境 |
|---|---|
| 当前类目录 | 仅开发环境,打成jar后无效 |
| resources中的win32目录 | 仅开发环境,打成jar后无效 |
| 当前运行目录 | 开发+生产环境 |
| jdk的bin目录 | 开发+生产环境 |
| 系统system32目录 | 开发+生产环境 |
此类寻找规则依据jna的版本而定,在早先的jna版本里不支持resource中的win32目录查找
4. 打包发布
因为dll库的特殊依赖关系,需要将dll一起打包,我常用的做法是
- resources中放入dll文件一起打包
- 所有的Native.load写入完整路径
- 项目启动后在调用Native.load前先将resource中的dll文件复制到运行磁盘的指定目录下(不可直接访问jar包resource中的文件)
使用这种打包方案就能将dll文件同步到每个版本的jar包且不需要再到其他地方维护dll文件,但缺点就是打出来的jar包会相对较大
有兴趣的小伙伴可以关注公众号【暴走的怪兽君】,常更新Java干货资讯,免费提供大量教程和工具下载。