JNI的又一替代者—使用JNR访问Java外部函数接口(jnr-ffi)
1. JNR简单介绍
继上文“JNI的替代者—使用JNA访问Java外部函数接口”,我们知道JNI越来越不受欢迎,JNI是编写Java本地方法以及将Java虚拟机嵌入本地应用程序的标准编程接口。它管理着JVM和非托管的本地环境之间的边界,提供数据编组和对象生命周期管理协议。
根据JEP(JDK增强提案) 191,JNI在下列几个方面最令开发人员痛苦:
- 需要开发人员编写C代码,这意味着他们需要具备一个完全不同于Java的世界的专业知识。
- 由于开发人员必须对JVM如何管理内存和代码多少有一些了解,所以典型的C和Java开发人员通常并不具备使用JNI所需的专业知识。
- 开发人员必须能够为他们想要支持的每个平台构建代码,或者为终端用户提供适当的工具,由他们来完成这项工作。
- 相比于相同的库绑定到本地应用程序,基于JNI的库性能通常较差。
- JNI充当了一个不透明的安全边界。JDK并不知道库中的函数可能会调用什么,或者库中的代码是否会损害JVM的稳定或安全。
FFI API将提供下列特性:
- 一个描述本地库调用和本地内存结构的元数据系统。
- 发现和加载本地库的机制。
- 基于元数据将库/函数或内存结构绑定到Java端点的机制。
- 用于Java数据类型和本地数据类型之间编组和解组的代码。
上面段落来自JEP 191的描述(由参考文献(1)翻译),由此可见虽然JNA使用广泛,但JNR可能更渐趋势,也许在不久的将来JNR-FFI(jffi)就会内建在JDK中与JNI一样成为Java访问外部函数的标准接口。因此,学习使用JNR是非常有必要的。
3. JNR简单实例
将打包好的jar文件加到Eclipse中,还是以“Hello World”为例,这次用C中的puts()函数打印,如下:
package helloworld; import jnr.ffi.LibraryLoader; public class HelloWorld { public static interface LibC { int puts(String s); } public static void main(String[] args) { LibC libc = LibraryLoader.create(LibC.class).load("msvcrt"); libc.puts("Hello, World"); } }
(1)定义一个静态接口
与JNA不同的是,该静态接口不用继承JNR中的某个类,更加简单。
接口里的内容就是你要用的动态链接库函数原型,同样的,该原型必须与C/C++中的保持一致,这同样是技术难点(详见上篇文章中的技术难点详述)。
(2)如何调用声明的外部函数
首先通过LibraryLoader.create().laod()得到该接口的一个实例,然后通过该实例直接调用里面的方法即可。
LibraryLoader.create().load()中第一个括号里是该接口的Class类型,第二个括号是要加载的动态链接库名称,同样没有.dll/.so后缀。这两个参数与JNA下的两个参数是一样的,使用情况也是一样。
Java的类型与C类型的对应关系为:
- byte - 8 bit signed integer
- short - 16 bit signed integer
- int - 32 bit signed integer
- long - natural long (i.e. 32 bits wide on 32 bit systems, 64 bit wide on 64bit systems)
- float - 32 bit float
- double - 64 bit float
- String - equivalent to "const char *"
- Pointer - equivalent to "void *"
- Buffer - equivalent to "void *"
这只是JNR的入门使用,更多的使用方法还期待官方给出更多的例子和说明文档。
4. 参考文献
(1)Java 外部函数接口
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。