基于JNA框架实现JAVA与读写器通信的研究

时间:2022-10-09 02:04:24

基于JNA框架实现JAVA与读写器通信的研究

【摘要】众所周知,JAVA以跨平台著称,这也是许多程序员喜欢JAVA的原因,但JAVA也有其不足之处,它与硬件设备的通信一直是以使用JNI调用.dll/.so共享类库来实现的,程序员如果使用JNI技术调用.dll/.so文件,首先需要另外编写一个.dll/.so共享库来调用.dll/.so文件中公布的函数,这种调用dll/.so库中的原生函数的JAVA程序非常麻烦,所以对于程序员来说,非到不得已,不会轻易使用JNI技术。为解决JNI技术的繁琐性,本文介绍将一个最新的访问本机代码的JAVA框架—JNA,探讨如何使用JNA技术调用第三方dll文件;并利用这一框架实现与读写器的通信。

【关键词】JNA框架;读写器;通信

一、目前JAVA与硬件设备通信的现状及存在问题

JAVA跨平台的特性深受JAVA程序员的喜爱,这是JAVA的优越性,但是正是为了实现跨平台的目的,JAVA和本地系统的各种内在联系变得很少,这大大约束了它的功能,比如与一些硬件设备的通信,往往要花很大的精力去编写动态函数库来管理设备端口,JDK从1.1版本开始提供了解决这个问题的技术标准:JNI标准;和许多解释执行的语言一样,JAVA提供了调用原生函数的机制,以加强JAVA平台的能力,JavaTMNative Interface(JNI)就是JAVA调用原生函数的机制。

事实上,很多JAVA核心代码内部就是使用JNI实现的,这些JAVA功能实际上是通过原生函数提供的。但是,使用JNI对于JAVA开发者来说简直是一场恶梦;如果你已经有了原生代码,使用JNI,你必须用C语言重新编写一个动态库,这个动态库的唯一功能就是使用JAVA能理解的C代码来调用目标原生函数。一般情况下,设备厂商提供的硬件接口都已经经过一定的封装和处理,不能直接使用JAVA程序通过端口和设备进行通信,JAVA若想与设备进行通信,就必须使用JNI的方式重新编写动态函数库来调用硬件设备,而这种方法的繁冗程度也可想而知,开发效率也不高,因此,人们一直都视JNI为禁地,轻易不愿涉足。

二、解决JAVA与硬件设备通信的新方法

1.什么是JNA框架

JNA(Java Native Access)是一个开源的JAVA框架,由SUN公司主导开发的,建立在经典的JNI基础之上的一个框架,它提供一组JAVA工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个JAVA接口中描述目标native library的函数与结构,JNA将自动实现JAVA接口到native function的映射。[1]

JNA的项目地址:https:///,JNA使JAVA调用原生函数就像.NET上的P/Invoke一样方便快捷,极大的提高程序员编写代码的效率。JNA使JAVA平台可以方便的调用原生函数,这大大扩展了JAVA平台的整合能力,简化了开发难度,又增强了JAVA与硬件设备通信的功能。

2.JNA框架的工作原理

JNA是建立在JNI技术基础之上的一个JAVA类库,它使编程人员可以方便地使用JAVA直接访问动态链接库中的函数,从而实现对.dll/.so文件的访问。原来使用JNI,你必须手工用C写一个动态链接库,在C语言中映射JAVA的数据类型,而编写动态链接库的唯一用途就是使用JAVA能够理解的C代码来调用目标原生函数。同时编写JAVA和C代码的过程使开发的难度大大增加,而这个没其他用途的动态链接库的编写过程显得相当枯燥。JNI调用设备方法如图1所示。

JNA中,它提供了一个动态的C语言编写的转发器,可以自动实现JAVA和C的数据类型映射。作为程序员,不再需要编写C动态链接库,极大地简化了JAVA调用原生函数的过程。当然,这也意味着,使用JNA技术比使用JNI技术调用动态链接库会对性能略有影响,如可能在速度上会降低几倍,但影响并不大。从总体上来看,使用JNA是利远远大于弊的。JNA打破了JAVA和原生代码原本泾渭分明的界限,充分发挥各自擅长领域的分工合作,提高程序员开发的效率。从某种意义上讲,JNA从JNI中来,但却青出于蓝而胜于蓝,逐渐获得了广大开发人员的喜爱。其调用设备方法如图2所示。

3.JNA框架需要注意的细节及技术难点

(1)当前路径是在项目下,而不是bin输出目录下。JNA在搜索dll路径的时候首先是从项目的根路径开始查找,然后再搜索当前操作系统的全局路径,其次搜索path指定的路径。

(2)JNA所使用的数据类型属于JAVA的数据类型,而原生函数中的数据类型是由使用的编程语言决定的,有可能是C、Delphi等语言的数据类型。JAVA与C语言数据结构的对应关系如表1所示。

4.JNA编程过程

Dll是C函数的集合、容器,这正和接口的概念吻合。JNA把一个dll/.so文件看做是一个JAVA接口,JNA通过调用接口来实现与第三方dll的通信。下面我们将以一个例子来说明如何调用dll中的函数。

(1)首先我们定义这样一个接口

public interface CLibrary extends Library {

//当前路径是在项目下,而不是bin输出目录下。

//fkc60为需要调用的dll文件,不加后缀有利于平台无关性

CLibrary sdtapi = (CLibrary) Native.loadLibrary(

(Platform.isWin

-dows() ? .\\lib\\fkc60” : “c”), CLibrary.class);

//====================

==以下是构造dll的函数======

==========//

//打开串口函数

boolean USB_DevInit(int

port);

}

(2)分析过程如下所示

如果dll是以stdcall方式输出函数,那么就继承StdCallLibrary。否则就继承默认的Library接口。接口内部需要一个公共静态常量:sdtapi。

CLibrary sdtapi = (CLibrary) Native.loadLibrary(

(Platform.isWindows

() ? .\\lib\\fkc60" : "c"), CLibrary.class);

通过这个常量,就可以获得这个接口的实例,从而使用接口的方法。也就是调用外部dll的函数!注意:1) Native.loadLibrary()函数有2个参数:第一个参数是dll或者.so文件的名字,但不带后缀名。这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。第二个参数是本接口的Class类型。JNA通过这个Class类型,根据指定的dll/.so文件,动态创建接口的实例。2)接口中你只需要定义你需要的函数或者公共变量,不需要的可以不定义。

boolean USB_DevInit(int port);

参数和返回值的类型,应该和dll中的C函数的类型一致。这是JNA,甚至所有跨平台调用的难点。这里,C语言的函数参数是:int port;JNA中对应的JAVA类型也是int,所以我们在做跨平台的时候,在数据类型上的选择应该尽量做到简单,这有利于跨平台的实现。

5.JNA的不足与补充

我们已经见识了JNA的强大。但是,有些需求还是必须求助于JNI。JNA是建立在JNI技术基础之上的一个框架。使用JNI技术,不仅可以实现JAVA访问C函数,也可以实现C语言调用JAVA代码。而JNA只能实现JAVA访问C函数,作为一个JAVA框架,自然不能实现C语言调用JAVA代码。此时,你还是需要使用JNI技术。JNI是JNA的基础,是JAVA和C互操作的技术基础。

三、如何利用JNA框架实现与读写器的通信

目前市场上的大多硬件厂商提供的开发包是原生函数,比如读写设备就是这个情况,一般设备厂商会提供两种类型的类库文件,windows系统的会包含.dll/.h/.lib文件,而linux会包含.so/.a文件,这里只讨论windows系统下的c/c++编译的dll文件调用方法。

现在来讨论这样一个问题,我们现要为JAVA项目添加IC卡读写器功能,设备厂商提供了一个fkc60.dll动态库,下面以其中的二个函数为例:

1.打开串口函数

1)bool USB_DevInit(int port);

2)用途及说明:调用其它函数前先打开串口,成功返回true,失败返回false;

3)参数:port表示串行口,1为端口1,2为端口2,以此类推。

2.控制读写器发声函数

1)bool USB_BeepEx(int port, int ptype);

2)用途及说明:控制读写器发声;成功返回true,失败返回false;

3)参数:port表示串口号,1为端口1,2为端口2,以此类推,ptype表示发声类型0发短声,1发长声。

首先,你需要下载一个jna.jar包,就可以方便地调用动态链接库中的C函数了,在JAVA项目中引入jna.jar包,本例是把fkc60放在项目的lib目录下引入的。

其次,你需要创建一个类:

import com.sun.jna.Library;

import com.sun.jna.Native;

import com.sun.jna.Platform;

public interface CLibrary extends Library {

//当前路径是在项目下,而不是bin输出目录下。

//fkc60为需要调用的dll文件,不加后缀有利于平台无关性

CLibrary sdtapi = (CLi-

brary) Native.loadLibrary(

(Platform.isWin-

dows() ? .\\lib\\fkc60” : “c”), CLibrary.class);

//======================以下是构造dll的函数==========

======//

//打开串口函数

boolean USB_DevInit(int port);

//寻卡函数

int USB_Request(int tagType);

//控制读写器发声的函数,port表示串口号,1为端口1,2为端口2

//ptype表示发声类型0发短声,1发长声。

boolean USB_BeepEx(int port,int ptype);

//关闭串口函数

boolean USB_DevClose();

}

public class test {

/**

* 以下是调用测试函数

*/

public static void main(String

[] args) {

try {

//构造一个调用对象,打开串口函数

boolean result = CLibrary.sdtapi.USB_DevInit(2);

System.out.println("result:"+result);

//构造一个调用对象,打开串口函数

int requestData=CLibra

-ry.sdtapi.USB_Request(1);

System.out.println("request:"+requestData);

//构造一个调用对象,打开串口函数,成功发声返回true

boolean Beep=CLibrary.sdtapi.USB_BeepEx(5, 0);

System.out.println("Beep:"

+ Beep);

} catch (Exception e) {

e.printStackTrace();

}

}

}

最后执行可以看到控制台中打印串行口打开成功信息,并听到读写器发出了短声。

四、结束语

JNA技术相对于JNI技术确实提高了开发的效率,并且扩展了JAVA的功能,但它仍存在着一个缺陷,即破坏了JAVA程序的最重要优点:平台无关性,所以除非必须(不得不)使用JNA技术,一般还是提倡写100%纯JAVA程序,根据自己的经验和查阅的一些资料,把可以使用JNA的情况罗列如下:

1.需要直接操作物理设备,而没有相关的驱动程序;

2.用JAVA会产生系统难以支付的开销,如需要大量网络链接的场合;

3.存在大量可重用的C/C++代码,通过JNA可以减少开发工作量,避免重复开发。

参考文献

[1]沈东良.深入浅出JNA—快速调用原生函数[J].程序员,2009,3.

[2]匿名.JNA—JNI终结者.

作者简介:刘魁元(1971—),男,广东大埔人,讲师,主要从事信息化教育、信息技术理论与实践研究,精通一卡通软硬件。

上一篇:国投郑煤教学三矿泵房自动化系统设计 下一篇:普通高校大学生信息素质教育的现状分析