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的稳定或安全。
因此JNI创建本地函数的方式并不简单,于是产生了像Java Native Access(JNA)和Java Native Runtime(JNR)这样的库。JNA和JNR都是基于JNI创建的,而JEP 191定义的Java Foreign Function Interface(FFI)可能会基于JNR。使用FFI API而不是JNI绑定本地代码和内存将成为开发人员更喜欢的方式。

FFI API将提供下列特性:

  • 一个描述本地库调用和本地内存结构的元数据系统。
  • 发现和加载本地库的机制。
  • 基于元数据将库/函数或内存结构绑定到Java端点的机制。
  • 用于Java数据类型和本地数据类型之间编组和解组的代码。
对Java FFI的需求已经产生了JNA和JNR库。JNA库应用更广泛(具体使用参见“JNI的替代者—使用JNA访问Java外部函数接口”)。JNR库更全面,因为它实现了不同层次的抽象,提供了函数和内存元数据,对库和函数绑定进行了抽象。JNR已经在JRuby项目中大量使用,它可能会成为JEP 191的基础。

上面段落来自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 外部函数接口

(2)Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法

(3)如何将maven项目打包成可执行的jar

JNI的又一替代者—使用JNR访问Java外部函数接口(jnr-ffi),古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。