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是非常有必要的。

JNR-FFI项目也托管自Github,其使用方法与JNA差不多,不过JNR并没有给出相应的jar包,需要我们自己打包使用。

2. JNR项目打包(jnr-ffi.jar)——如何打包Github上的maven项目

首先要明确,Github上托管的项目一般是用maven管理构建的,而不是Eclipse/MyEclipse,因此如果你想通过从Github
上直接下载项目源码(Download Zip的方式下载)然后导入或拷贝进Eclipse里打包是行不通的。我一开始也是这么做的,发现项目不完整,缺
少一些包,因此打成的jar包也是不能用的。

让我惊讶的,在maven官方库里的jnr-ffi.jar包也是不完整的,下载下来也不能用,还有这个地方的所有jnr包,我都试过了,全部不完整,因此只能自己打包。

在打包之前,你首先需要将完整的源码下载下来,然后有两种方式打包成jar文件。

  • 将maven项目导入Eclipse中打包
  • 通过maven命令mvn打包

两种方法都有需要注意的地方。不熟悉maven的人可以采取第一种方式,上手简单。熟悉maven的当然推荐用mvn命令打包,不过需要注意这里有第三方依赖包,不是一句简单的命令就可搞定。

将maven项目导入Eclipse中打包

注意:虽然Eclipse内置了Maven插件,但表示不太好用,经常出现问题,建议卸载Eclipse的自带的maven插件,然后安装第三方的m2eclipse插件,该插件目前有效的安装地址为:http://download.eclipse.org/technology/m2e/releases,通过Eclipse中Help—Install New
Software...—Add Repository安装即可。

有了maven插件后,打包的具体步骤如下:

(1)从Github下载源码

这个其实非常关键,因为不能通过“Download Zip”的方式直接从Github网页上下载,这样下载的源码缺少很多j依赖的ar包,需要通过git clone的方式下载

git clone https://github.com/jnr/jnr-ffi.git

下载后的项目源码就在当前命令行路径下。

(2)导入maven项目

将刚下载的完整的jnr源码导入到Eclipse中,注意导入的是Maven项目

选择刚下载的项目根路径

这里出现了错误,如果没错的就可以直接打包了,如果跟我一样出现下面的错误,那么请继续

从出错信息可以看出是缺少Maven-antrun插件,这是Maven的ant插件
用来自动构建项目的,没有这个插件,maven配置文件pom.xml中的<execution></execution>之间
的任务就执行不了,因此如果忽略这个出错继续点“Finish”那么pom.xml文件就有错误,具体的出错信息如下:

Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-antrun-plugin:1.1:run
 (execution: default, phase: test-compile)

这里有官方给出的解决方案,我就直接用第一种方法:在<plugins>前面加上<pluginManagement>,在</plugins>后面加上加上</pluginManagement>  即可。

其实我的Eclipse工程里还有另外一个错误,就是在NativeClosureFactory.java文件中:

The method expunge(NativeClosureFactory.ClosureReference, Integer) in the type NativeClosureFactory is not applicable for
the arguments (NativeClosureFactory<T>.ClosureReference, Integer)

属于Java泛型错误,不知道完整的代码你可能不知道具体的问题所在,下面举个简单的例子:

你能看出问题所在吗?Native类是个泛型类,但在其内置类Ref中使用时没有加上泛型的标志,将Native当作普通类使用,忽略了泛型<T>标志。其实这可能与Java编译器有关,有的版本可能不会报这个错,那么改正方法也很简单,将

privatefinalNative factory;privateRef(Native factory){

改成

privatefinalNative<T> factory;privateRef(Native<T> factory){

即可。

至此,项目没有任何错误产生了,就可以开始打包了(据我测试,前面的两个错误不改正直接打包其实也没什么关系,jar包照样能用,但是知错改错我们能学到更多额外的东西)。

(3)用Build fat jar 打包

这里为什么说要用“Build fat jar”工具打包而不是直接的export出jar包的方式打包呢?因为该工程依赖了很多其它的第三方
jar包,如果直接export而不作配置,这些依赖的jar包不会被打进去,也就错了,需要自定义配置文件MANIFEST.MF,有些麻烦,具体配置
可参考“Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法”。

使用Fat jar打包插件就不一样了,无需任何配置,一键打包,该插件安装方法也请参考上述文章:

修改jar包文件,加上目前的版本号即可。可以看到用Eclipse打包还是挺麻烦的,至少我遇到了N多问题,因此推荐用mvn命令打包。

通过maven命令mvn打包

如果你机子上没有安装maven,那么请首先到这里下载其二进制包,无需安装,只要解压到某个路径下,然后将其路径添加到环境变量PATH中即可在任何地方使用。

命令行进入到jnr-ffi所在根目录,一般用mvn命令打jar命令如下即可:

mvn jar:jar

但是这样的不对的,该命令打成的jar包不包含依赖的第三方jar文件,因此是错误的。其实我发现在网上找到的所有jnr-ffi的jar包都是直接用这个命令打包的,因此全部不能用。


正确的打包方式是:

将包含第三方依赖jar的maven项目打包成jar文件有两种方法,我这里使用比较简单的方法:使用maven-assembly-plugin打包,步骤如下:

(1)pom.xml添加assembly插件

      <plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs> </configuration>
</plugin>

由于第三方jar没有main文件,所以不需要加manifest。

(2)执行如下命令

mvn assembly:assembly

这样就在jnr-ffi根目录下的target文件夹里生成一个jnr-ffi-2.0.0-SNAPSHOT-jar-with-dependencies.jar文件。

这就是我们所需要的jar文件。

不管如何,如果你打包不顺利的话,这里有我打的jnr-ffi_2.0.0jar包下载地址

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)的更多相关文章

  1. JNI的替代者—使用JNA访问Java外部功能接口

    摘自:http://www.cnblogs.com/lanxuezaipiao/p/3635556.html JNI的替代者-使用JNA访问Java外部功能接口 1. JNA简单介绍 先说JNI(Ja ...

  2. 【C/C++开发】【Java开发】JNI的替代者—使用JNA访问Java外部功能接口

    JNI的替代者-使用JNA访问Java外部功能接口 1. JNA简单介绍 先说JNI(Java Native Interface)吧,有过不同语言间通信经历的一般都知道,它允许Java代码和其他语言( ...

  3. 如何让Java和C++接口互相调用:JNI使用指南

    如何让Java和C++接口互相调用:JNI使用指南 转自:http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos ...

  4. JNI/NDK开发指南(二)——JVM查找java native方法的规则

    通过第一篇文章,大家明白了调用native方法之前,首先要调用System.loadLibrary接口加载一个实现了native方法的动态库才能正常访问,否则就会抛出java.lang.Unsatis ...

  5. cocos2d-x 通过JNI实现c/c++和Android的java层函数互调

    文章摘要: 本文主要实现两个功能: (1)通过Android sdk的API得到应用程序的包名(PackageName),然后传递给c++层函数. (2)通过c++函数调用Android的java层函 ...

  6. IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用

    本文学习如何在C代码中创建Java对象和对象数组,前面我们学习了C代码中访问Java对象的属性和方法,其实在创建对象时本质上也就是调用构造函数,因此本文知识学习起来也很轻松.有了前面学习数组创建的方法 ...

  7. Android 通过 JNI 访问 Java 字段和方法调用

    在前面的两篇文章中,介绍了 Android 通过 JNI 进行基础类型.字符串和数组的相关操作,并描述了 Java 和 Native 在类型和签名之间的转换关系. 有了之前那些基础,就可以实现 Jav ...

  8. Android JNI访问Java成员

    在 JNI 调用中,不仅仅 Java 可以调用本地方法,本地方法也可以调用 Java 中的方法和成员变量. Java 中的类封装了属性和方法,想要访问 Java 中的属性和方法,首先要获得 Java ...

  9. android JNI 一维数组、二维数组的访问与使用

    在JNI中访问JAVA类中的整型.浮点型.字符型的数据比较简单,举一个简单的例子,如下: //得到类名 jclass cls = (*env)->GetObjectClass(env, obj) ...

随机推荐

  1. re模块,subprocess模块

    """ RE是什么 正则 表达 式子 就是一些带有特殊含义的符号或者符号的组合 它的作用是对字符串进行过滤 在一堆字符串中找到你所关心的内容 你就需要告诉计算机你的过滤规 ...

  2. uwsgi_read_timeout超时处理

    最近发现一服务器一个奇怪的现象: Django的视图函数在浏览器一个请求的情况下,竟然做了两个请求的函数处理.不可思议,找了几天也不知道为什么, 只发现只要用uwsgi_read_timeout之后, ...

  3. 闭合浮动的方法css

    浮动是一个有意思(你也可以说它很麻烦)的CSS属性,任何元素设置了浮动,层级就提高了,会影响它后面没设置浮动的元素,这些倒霉的被影响者会跑到浮动层的下面去(当然IE6.IE7除外),那解决方法呢? 常 ...

  4. STL容器之一vector

    STL中最简单也是最有用的容器之一是vector<T>类模板,称为向量容器,是序列类型容器中的一种. 1.vector<T> 对象的基本用法(1)声明:vector<ty ...

  5. hdu 2688

    这题,因为要统计之前有多少个数比当前的数小,所以我用的树状数组 基本代码就是这样,还是比较好想的,只不过我没想出来罢了 #include <iostream> #include <c ...

  6. unigui 设置单元格颜色

    procedure TF_Resource2.UniDBGrid1DrawColumnCell(Sender: TObject; ACol,  ARow: Integer; Column: TUniD ...

  7. 利用阿里大于接口发短信(Delphi版)

    阿里大于是阿里通信旗下产品,融合了三大运营商的通信能力,提供包括短信.语音.流量直充.私密专线.店铺手机号等个性化服务.每条四分五,价钱还算公道,经老农测试,响应速度非常快,基本上是秒到.官方文档提供 ...

  8. delphi 分享三个随机字符串

    uses math; function GenID:String; var b, x: byte; begin Result := '{'; Randomize; do begin ) > ,) ...

  9. 分形之二叉树(Binary Tree)

    上一篇文章讲的是分形之树(Tree),这一篇中将其简化一下,来展示二叉分形树的生长过程. 核心代码: static void FractalBinaryTree(const Vector3& ...

  10. [转载]腾讯专家:论高级DBA的自我修养

    作者介绍: 张秀云:2007年开始从事运维方面的工作,经历过网络管理员.linux运维工程师.DBA.分布式存储运维等多个IT职位.对linux运维.mysql数据库.分布式存储有丰富的经验.2012 ...