继上文“JNI的替代者—使用JNA访问Java外部函数接口”,我们知道JNI越来越不受欢迎,JNI是编写Java本地方法以及将Java虚拟机嵌入本地应用程序的标准编程接口。它管理着JVM和非托管的本地环境之间的边界,提供数据编组和对象生命周期管理协议。
根据JEP(JDK增强提案) 191,JNI在下列几个方面最令开发人员痛苦:
FFI API将提供下列特性:
上面段落来自JEP 191的描述(由参考文献(1)翻译),由此可见虽然JNA使用广泛,但JNR可能更渐趋势,也许在不久的将来JNR-FFI(jffi)就会内建在JDK中与JNI一样成为Java访问外部函数的标准接口。因此,学习使用JNR是非常有必要的。
JNR-FFI项目也托管自Github,其使用方法与JNA差不多,不过JNR并没有给出相应的jar包,需要我们自己打包使用。
首先要明确,Github上托管的项目一般是用maven管理构建的,而不是Eclipse/MyEclipse,因此如果你想通过从Github上直接下载项目源码(Download Zip的方式下载)然后导入或拷贝进Eclipse里打包是行不通的。我一开始也是这么做的,发现项目不完整,缺少一些包,因此打成的jar包也是不能用的。
让我惊讶的,在maven官方库里的jnr-ffi.jar包也是不完整的,下载下来也不能用,还有这个地方的所有jnr包,我都试过了,全部不完整,因此只能自己打包。
在打包之前,你首先需要将完整的源码下载下来,然后有两种方式打包成jar文件。
两种方法都有需要注意的地方。不熟悉maven的人可以采取第一种方式,上手简单。熟悉maven的当然推荐用mvn命令打包,不过需要注意这里有第三方依赖包,不是一句简单的命令就可搞定。
注意:虽然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泛型错误,不知道完整的代码你可能不知道具体的问题所在,下面举个简单的例子:
public final class Native<T> { private void test1(Ref ref, Integer key) { } final class Ref { private final Native factory; private Ref(Native factory) { this.factory = factory; } public void test2() { factory.test1(this, 1); } } }
private final Native factory; private Ref(Native factory) {
改成
private final Native<T> factory; private Ref(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,那么请首先到这里下载其二进制包,无需安装,只要解压到某个路径下,然后将其路径添加到环境变量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包下载地址:http://download.csdn.net/detail/lanxuezaipiao/7126127
将打包好的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"); } }
与JNA不同的是,该静态接口不用继承JNR中的某个类,更加简单。
接口里的内容就是你要用的动态链接库函数原型,同样的,该原型必须与C/C++中的保持一致,这同样是技术难点(详见上篇文章中的技术难点详述)。
(2)如何调用声明的外部函数
首先通过LibraryLoader.create().laod()得到该接口的一个实例,然后通过该实例直接调用里面的方法即可。
LibraryLoader.create().load()中第一个括号里是该接口的Class类型,第二个括号是要加载的动态链接库名称,同样没有.dll/.so后缀。这两个参数与JNA下的两个参数是一样的,使用情况也是一样。
Java的类型与C类型的对应关系为:
这只是JNR的入门使用,更多的使用方法还期待官方给出更多的例子和说明文档。
(1)Java 外部函数接口
(2)Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法
(3)如何将maven项目打包成可执行的jar