MES系列--(2)JNA使用介绍

来源:互联网 发布:风云排名知乎 编辑:程序博客网 时间:2024/05/16 12:29

JNA:java native access! 有人说是JNI的升级版,其实用过后,你发现不是这样的,JNA没有JNI强大,在某些情况下,用起来比JNI简单一些而已。上篇JNI入门,大家可以看到编写JNI去访问本地代码的步骤已经很规矩死板了,如果你提前有了本地代码如dll文件,那“恭喜”你,你得封装一层了,JNI中,你本地代码的函数名称,参数都是有要求的!JNA就在这里有了突破,你可以先拥有本地代码,然后写Java代码去适配本地代码,而不是JNI中让本地代码去适配你的Java代码。

我在实际应用中,因为大多是先有本地代码(硬件厂商提供的硬件驱动),所以JNA还是能发挥一定作用,直接上例子吧。我们现在有一个IC卡读卡器,硬件厂商提供了访问硬件的dll文件(Windows平台),其中对应的一个函数定义为:

#define MAX_BUFFER_SIZE 2048#define MYLIBAPI  extern   "C"   __declspec( dllexport )  MYLIBAPI char* readCardId(int, int);


函数readCardId就是这个dll文件的导出函数,接受两个int类型参数,返回char数组,即卡号。这个函数是不符合JNI规范的,如果我们使用JNI去访问这个函数,是必须封装一层去转调这个函数。但有了JNA,我们有了另一中选择。我们可以直接Java代码,先把这段Java代码贴出来,再给大家分析:

package cn.test;import com.sun.jna.Library;import com.sun.jna.Native;public interface IReadCard extends Library{public static final IReadCard INSTANCE = (IReadCard)Native.loadLibrary("ICCardReader", IReadCard.class);String readCardId(int antenum, int readcount);}


这是一个Java接口,继承自Library,来自JNA相关的jar包(我的资源中有下载)。接口中定义了一个函数,String readCardId (int , int),这个函数必须要和dll文件中的函数进行对应(JNI中定义了Java和C++之间的类型对应关系,大家可以参考JDK相关文档)。然后这个接口中通过JNA中的类Native.loadLibrary去加载相关的dll文件,本例中为ICCardReader,并返回一个接口实例对象!给实例对象的方法调用就会调到本地方法中。注意,加载本地代码库文件时,不要加后缀,这是为平台的无关性,Windows平台下,库文件为dll,而Linux下为so后缀!

JNA的编写确实很快,因为你直接去写Java来适配本地代码即可!写完了,就可以调用了。但有些情况,建议还是封装一层:如果你的本地代码中函数的参数类型很复杂,无法或很难和Java这边进行对应,建议你还是重新封装一下,提供一个参数简单的函数供JNA这边使用,毕竟不同语言间类型这块的转换很容易出现问题,尤其对复杂类型。

再稍微谈一下JNA吧,JNA底层是完全基于JNI的,JNA提供的jar包负责的就是这种调用间转换。其实最后的调用过程还是JNI那套。所以JNA的效率会低于JNI。并且因为JNA对于本地代码没有要求,所以本地代码是无法回调Java端代码的,JNI中本地代码可以回调Java中的代码。

总结一下,JNA对于一些函数参数和返回值比较简单的本地代码的调用非常便捷,并且其中不能回调Java端代码(这个实际用的也不多)。如果本地代码的参数和返回值比较复杂,使用JNA建议也要封装一层。

最后再稍带提一下:跨平台调用,最难解的地方就是数据类型的转换了!所以如果你认为某个本地代码库是为了Java平台去调用,那么就提前保证其中函数参数的简单性吧,不要用好多复杂的参数!

原创粉丝点击