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

上传人:宝路 文档编号:20927228 上传时间:2017-11-22 格式:DOCX 页数:7 大小:416.44KB
返回 下载 相关 举报
JNI的替代者—使用JNA访问Java外部功能接口_第1页
第1页 / 共7页
JNI的替代者—使用JNA访问Java外部功能接口_第2页
第2页 / 共7页
JNI的替代者—使用JNA访问Java外部功能接口_第3页
第3页 / 共7页
JNI的替代者—使用JNA访问Java外部功能接口_第4页
第4页 / 共7页
JNI的替代者—使用JNA访问Java外部功能接口_第5页
第5页 / 共7页
点击查看更多>>
资源描述

《JNI的替代者—使用JNA访问Java外部功能接口》由会员分享,可在线阅读,更多相关《JNI的替代者—使用JNA访问Java外部功能接口(7页珍藏版)》请在金锄头文库上搜索。

1、JNI 的替代者 使用 JNA 访问 Java 外部功能接口 1. JNA 简单介绍先说 JNI(Java Native Interface)吧,有过不同语言间通信经历的一般都知道,它允许 Java 代码和其他语言(尤其 C/C+)写的代码进行交互,只要遵守调用约定即可。首先看下 JNI 调用 C/C+的过程,注意写程序时自下而上,调用时自上而下。可 见步骤非常的多,很麻烦,使用 JNI 调用.dll/.so 共享库都能体会到这个痛苦的过程。如果已有一个编译好的.dll/.so 文件,如果使用 JNI 技 术调用,我们首先需要使用 C 语言另外写一个.dll/.so 共享库,使用 SUN 规定

2、的数据结构替代 C 语言的数据结构,调用已有的 dll/so 中公布的函 数。然后再在 Java 中载入这个库 dll/so,最后编写 Java native 函数作为链接库中函数的代理。经过这些繁琐的步骤才能在 Java 中调用 本地代码。因此,很少有 Java 程序员愿意编写调用 dll/.so 库中原生函数的 java 程序。这也使 Java 语言在客户端上乏善可陈,可以说 JNI 是 Java 的一大弱点!那么 JNA 是什么呢?JNA(Java Native Access)是一个开源的 Java 框架,是 Sun 公司推出的一种调用本地方法的技术,是建立在经典的 JNI 基础之上的一

3、个框架。之所以说它是 JNI 的替 代者,是因为 JNA 大大简化了调用本地方法的过程,使用很方便,基本上不需要脱离 Java 环境就可以完成。如果要和上图做个比较,那么 JNA 调用 C/C+的过程大致如下:可以看到步骤减少了很多,最重要的是我们不需要重写我们的动态链接库文件,而是有直接调用的 API,大大简化了我们的工作量。JNA 只需要我们写 Java 代码而不用写 JNI 或本地代码。功能相对于 Windows 的 Platform/Invoke 和 Python的 ctypes。2. JNA 技术原理JNA 使用一个小型的 JNI 库插桩程序来动态调用本地代码。开发者使用 Java

4、接口描述目标本地库的功能和结构,这使得它很容易利用本机平台的功能,而不会产生多平台配置和生成 JNI 代码的高开销。这样的性能、准确性和易用性显然受到很大的重视。此外,JNA 包括一个已与许多本地函数映射的平台库,以及一组简化本地访问的公用接口。注意:JNA 是建立在 JNI 技术基础之上的一个 Java 类库,它使您可以方便地使用 java 直接访问动态链接库中的函数。原来使用 JNI,你必须手工用 C 写一个动态链接库,在 C 语言中映射 Java 的数据类型。JNA 中,它提供了一个动态的 C 语言编写的转发器,可以自动实现 Java 和 C 的数据类型映射,你不再需要编写C 动态链接库

5、。也许这也意味着,使用 JNA 技术比使用 JNI 技术调用动态链接库会有些微的性能损失。但总体影响不大,因为 JNA 也避免了 JNI 的一些平台配置的开销。3. JNA 简单使用JNA 的项目已迁移至 Github,目前最新版本是 4.1.0,已有打包好的 jar 文件可供下载。JNA 把一个.dll/.so 文件看做是一个 Java 接口,下面以一个简单的实例来说明怎么使用。当然要从最经典的 HelloWorld 开始,我们调用 C 的 printf 函数打印出“HelloWorld”(官方的例子),前提是已将 jar 包加入你的 classpath。package com.sun.jn

6、a.examples;import com.sun.jna.Library;import com.sun.jna.Native;import com.sun.jna.Platform;/* Simple example of JNA interface mapping and usage. */public class HelloWorld / This is the standard, stable way of mapping, which supports extensive/ customization and mapping of Java to native types.publi

7、c interface CLibrary extends Library CLibrary INSTANCE = (CLibrary)Native.loadLibrary(Platform.isWindows() ? msvcrt : c),CLibrary.class);void printf(String format, Object. args);public static void main(String args) CLibrary.INSTANCE.printf(Hello, Worldn);for (int i=0;i args.length;i+) CLibrary.INSTA

8、NCE.printf(Argument %d: %sn, i, argsi);运行程序,如果没有带参数则只打印出“Hello, World”,如果带了参数,则会打印出所有的参数。很简单,不需要写一行 C 代码,就可以直接在 Java 中调用外部动态链接库中的函数!下面来解释下这个程序。(1)需要定义一个接口,继承自 Library 或 StdCallLibrary默认的是继承 Library ,如果动态链接库里的函数是以 stdcall 方式输出的,那么就继承 StdCallLibrary,比如众所周知的 kernel32 库。比如上例中的接口定义:public interface CLibr

9、ary extends Library (2)接口内部定义接口内部需要一个公共静态常量:INSTANCE,通过这个常量,就可以获得这个接口的实例,从而使用接口的方法,也就是调用外部 dll/so 的函数。该常量通过 Native.loadLibrary()这个 API 函数获得,该函数有 2 个参数: 第 一个参数是动态链接库 dll/so 的名称,但不带.dll 或.so 这样的后缀,这符合 JNI 的规范,因为带了后缀名就不可以跨操作系统平台了。搜索动态链 接库路径的顺序是:先从当前类的当前文件夹找,如果没有找到,再在工程当前文件夹下面找 win32/win64 文件夹,找到后搜索对应的

10、dll 文件,如果 找不到再到WINDOWS 下面去搜索,再找不到就会抛异常了。比如上例中 printf 函数在 Windows 平台下所在的 dll库名称是 msvcrt,而在 其它平台如 Linux 下的 so 库名称是 c。 第二个参数是本接口的 Class 类型。JNA 通过这个 Class 类型,根据指定的.dll/.so 文件,动态创建接口的实例。该实例由 JNA 通过反射自动生成。CLibrary INSTANCE = (CLibrary)Native.loadLibrary(Platform.isWindows() ? msvcrt : c),CLibrary.class);接

11、口中只需要定义你要用到的函数或者公共变量,不需要的可以不定义,如上例只定义 printf 函数:void printf(String format, Object. args);注意参数和返回值的类型,应该和链接库中的函数类型保持一致。(3)调用链接库中的函数定义好接口后,就可以使用接口中的函数即相应 dll/so 中的函数了,前面说过调用方法就是通过接口中的实例进行调用,非常简单,如上例中:CLibrary.INSTANCE.printf(Hello, Worldn);for (int i=0;i args.length;i+) CLibrary.INSTANCE.printf(Argume

12、nt %d: %sn, i, argsi);这就是 JNA 使用的简单例子,可能有人认为这个例子太简单了,因为使用的是系统自带的动态链接库,应该还给出一个自己实现的库函数例子。其实我觉得这个完全没有必要,这也是 JNA 的方便之处,不像 JNI 使用用户自定义库时还得定义一大堆配置信息,对于 JNA 来说,使用用户自定义库与使用系统自带的库是完全一样的方法,不需要额外配置什么信息。比如我在 Windows 下建立一个动态库程序:#include stdafx.hextern C_declspec(dllexport) int add(int a, int b);int add(int a, i

13、nt b) return a + b;然后编译成一个 dll 文件(比如 CDLL.dll),放到当前目录下,然后编写 JNA 程序调用即可:public class DllTest public interface CLibrary extends Library CLibrary INSTANCE = (CLibrary)Native.loadLibrary(CDLL, CLibrary.class);int add(int a, int b);public static void main(String args) int sum = CLibrary.INSTANCE.add(3, 6

14、);System.out.println(sum);4. JNA 技术难点有过跨语言、跨平台开发的程序员都知道,跨平台、语言调用的难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败,都是这个问题造成的。关于这一点,不论何种语言,何种技术方案,都无法解决这个问题。JNA 也不例外。上面说到接口中使用的函数必须与链接库中的函数原型保持一致,这是 JNA 甚至所有跨平台调用的难点,因为 C/C+的类型与 Java 的类型是不一样的,你必须转换类型让它们保持一致,比如 printf 函数在 C 中的原型为:void printf(const char *format, argum

15、ent);你不可能在 Java 中也这么写,Java 中是没有 char *指针类型的,因此 const char *转到 Java 下就是 String类型了。这就是类型映射(Type Mappings),JNA 官方给出的默认类型映射表如下:还有很多其它的类型映射,需要的请到 JNA 官网查看。另外,JNA 还支持类型映射定制,比如有的 Java 中可能找不到对应的类型(在 Windows API 中可能会有很多类型,在 Java 中找不到其对应的类型),JNA 中 TypeMapper 类和相关的接口就提供了这样的功能。5. JNA 能完全替代 JNI 吗?这可能是大家比较关心的问题,但是遗憾的是,JNA 是不能完全替代 JNI 的,因为有些需求还是必须求助于 JNI。使用 JNI 技术,不仅可以实现 Java 访问 C 函数,也可以实现 C 语言调用 Java 代码。而 JNA 只能实现 Java 访问 C 函数,作为一个 Java 框架,自然不能实现 C 语言调用 Java 代码。此时,你还是需要使用 JNI 技术。JNI 是 JNA 的基础,是 Java 和 C 互操作的技术基础。有时候,你必须回归到基础上来。6. 参考文献(1)JNA JNI 终结者(2) C+DLL 编程详解

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号