JNI是Java调用原生函数的唯一机制,JNA就是建立在JNI之上,JNA简化了Java调用原生函数的过程。JNA提供了一个动态的C语言编写的转发器(实际上也是一个动态链接库,在Linux-i386中文件名是:libjnidispatch.so)可以自动实现Java与C之间的数据类型映射。从性能上会比JNI技术调用动态链接库要低。
使用JNA首先需要下载jna的jar包放到你的lib文件下。
如果是maven项目可以在pom.xml里面配置,如下所示:
<!-- https://mvnrepository.com/artifact/com.sun.jna/jna -->
<dependency>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
<version>3.0.9</version>
</dependency>
JNA编程过程
JNA把一个dll/.so文件看做是一个Java接口。
Dll是C函数的集合、容器,这正和接口的概念吻合。
我们定义这样一个接口,
public interface TestDll1 extends Library {
/**
* 当前路径是在项目下,而不是bin输出目录下。
*/
TestDll1 INSTANCE = (TestDll1)Native.loadLibrary("TestDll1", TestDll1.class);
// testdll Instance = (testdll) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
// testdll.class)
public void say(WString value);
}
如果dll是以stdcall方式输出函数,那么就继承StdCallLibrary。否则就继承默认的Library接口。
接口内部需要一个公共静态常量:instance。
TestDll1 INSTANCE = (TestDll1)Native.loadLibrary("TestDll1", TestDll1.class);
通过这个常量,就可以获得这个接口的实例,从而使用接口的方法。也就是调用外部dll的函数!
Native.loadLibrary第 一个参数是动态链接库dll/so的名称,但不带.dll或.so这样的后缀,这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。
搜索动态链 接库(dll)路径的顺序是:先从当前类的当前文件夹找,如果没有找到,再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,
如果 找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。比如上例中printf函数在Windows平台下所在的dll库名称是msvcrt,而在 其它平台如Linux下的so库名称是c
注意:
Native.loadLibrary()函数有2个参数:
1. dll或者.so文件的名字,但不带后缀名。这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。
搜索dll的路径是:
(1)项目的根路径
(2)操作系统的全局路径、C:\Windows\System32
(3)path指定的路径。 Native.loadLibrary(path,TestDll1.class);
path是用户自定义的项目目录。例如D:\\TestDll1
2.第二个参数是本接口的Class类型。
JNA通过这个Class类型,根据指定的dll/.so文件,动态创建接口的实例。
2.接口中你只需要定义你需要的函数或者公共变量,不需要的可以不定义。
public void say(WString value);
参数和返回值的类型,应该和dll中的C函数的类型一致。
这是JNA,甚至所有跨平台调用的难点。
这里,C语言的函数参数是:wchar_t*。
JNA中对应的Java类型是WStirng