先说JNI(Java Native Interface)吧,有过不一样语言间通讯经历的通常都知道,它容许Java代码和其余语言(尤为C/C++)写的代码进行交互,只要遵照调用约定便可。首先看下JNI调用C/C++的过程,注意写程序时自下而上,调用时自上而下。java
可 见步骤很是的多,很麻烦,使用JNI调用.dll/.so共享库都能体会到这个痛苦的过程。若是已有一个编译好的.dll/.so文件,若是使用JNI技 术调用,咱们首先须要使用C语言另外写一个.dll/.so共享库,使用SUN规定的数据结构替代C语言的数据结构,调用已有的 dll/so中公布的函 数。而后再在Java中载入这个库dll/so,最后编写Java native函数做为连接库中函数的代理。通过这些繁琐的步骤才能在Java中调用 本地代码。所以,不多有Java程序员愿意编写调用dll/.so库中原生函数的java程序。这也使Java语言在客户端上乏善可陈,能够说JNI是 Java的一大弱点!git
那么JNA是什么呢?程序员
JNA(Java Native Access)是一个开源的Java框架,是Sun公司推出的一种调用本地方法的技术,是创建在经典的JNI基础之上的一个框架。之因此说它是JNI的替 代者,是由于JNA大大简化了调用本地方法的过程,使用很方便,基本上不须要脱离Java环境就能够完成。github
若是要和上图作个比较,那么JNA调用C/C++的过程大体以下:编程
能够看到步骤减小了不少,最重要的是咱们不须要重写咱们的动态连接库文件,而是有直接调用的API,大大简化了咱们的工做量。数据结构
JNA只须要咱们写Java代码而不用写JNI或本地代码。功能相对于Windows的Platform/Invoke和Python的ctypes。app
JNA使用一个小型的JNI库插桩程序来动态调用本地代码。开发者使用Java接口描述目标本地库的功能和结构,这使得它很容易利用本机平台的功能,而不会产生多平台配置和生成JNI代码的高开销。这样的性能、准确性和易用性显然受到很大的重视。框架
此外,JNA包括一个已与许多本地函数映射的平台库,以及一组简化本地访问的公用接口。函数
注意:性能
JNA是创建在JNI技术基础之上的一个Java类库,它使您能够方便地使用java直接访问动态连接库中的函数。
原来使用JNI,你必须手工用C写一个动态连接库,在C语言中映射Java的数据类型。
JNA中,它提供了一个动态的C语言编写的转发器,能够自动实现Java和C的数据类型映射,你再也不须要编写C动态连接库。
也许这也意味着,使用JNA技术比使用JNI技术调用动态连接库会有些微的性能损失。但整体影响不大,由于JNA也避免了JNI的一些平台配置的开销。
JNA的项目已迁移至Github,目前最新版本是4.1.0,已有打包好的jar文件可供下载。
JNA把一个.dll/.so文件看作是一个Java接口,下面以一个简单的实例来讲明怎么使用。
固然要从最经典的HelloWorld开始,咱们调用C的printf函数打印出“HelloWorld”(官方的例子),前提是已将jar包加入你的classpath。
package com.sun.jna.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. public 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, World\n"); for (int i=0;i < args.length;i++) { CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]); } } }
运行程序,若是没有带参数则只打印出“Hello, World”,若是带了参数,则会打印出全部的参数。
很简单,不须要写一行C代码,就能够直接在Java中调用外部动态连接库中的函数!
下面来解释下这个程序。
Library
或StdCallLibrary
默认的是继承
Library
,若是动态连接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary
,好比众所周知的kernel32库。好比上例中的接口定义:
public interface CLibrary extends Library { }
接口内部须要一个公共静态常量:INSTANCE,
经过这个常量,就能够得到这个接口的实例,从而使用接口的方法,也就是调用外部dll/so的函数。
该常量经过Native.loadLibrary()这个API函数得到,该函数有2个参数:
CLibrary INSTANCE = (CLibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
接口中只须要定义你要用到的函数或者公共变量,不须要的能够不定义,如上例只定义printf函数:
void printf(String format, Object... args);
注意参数和返回值的类型,应该和连接库中的函数类型保持一致。
定义好接口后,就可使用接口中的函数即相应dll/so中的函数了,前面说过调用方法就是经过接口中的实例进行调用,很是简单,如上例中:
CLibrary.INSTANCE.printf("Hello, World\n"); for (int i=0;i < args.length;i++) { CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]); }
这就是JNA使用的简单例子,可能有人认为这个例子太简单了,由于使用的是系统自带的动态连接库,应该还给出一个本身实现的库函数例子。其实我以为这个彻底没有必要,这也是JNA的方便之处,不像JNI使用用户自定义库时还得定义一大堆配置信息,对于JNA来讲,使用用户自定义库与使用系统自带的库是彻底同样的方法,不须要额外配置什么信息。好比我在Windows下创建一个动态库程序:
#include "stdafx.h" extern "C"_declspec(dllexport) int add(int a, int b); int add(int a, int 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); System.out.println(sum); } }
有过跨语言、跨平台开发的程序员都知道,跨平台、语言调用的难点,就是不一样语言之间数据类型不一致形成的问题。绝大部分跨平台调用的失败,都是这个问题形成的。关于这一点,不论何种语言,何种技术方案,都没法解决这个问题。JNA也不例外。
上面说到接口中使用的函数必须与连接库中的函数原型保持一致,这是JNA甚至全部跨平台调用的难点,由于C/C++的类型与Java的类型是不同的,你必须转换类型让它们保持一致,好比printf函数在C中的原型为:
void printf(const char *format, [argument]);
你不可能在Java中也这么写,Java中是没有char *指针类型的,所以const char *转到Java下就是String类型了。
这就是类型映射(Type Mappings),JNA官方给出的默认类型映射表以下:
还有不少其它的类型映射,须要的请到JNA官网查看。
另外,JNA还支持类型映射定制,好比有的Java中可能找不到对应的类型(在Windows API中可能会有不少类型,在Java中找不到其对应的类型)
,JNA中TypeMapper类和相关的接口就提供了这样的功能。
这多是你们比较关心的问题,可是遗憾的是,JNA是不能彻底替代JNI的,由于有些需求仍是必须求助于JNI。
使用JNI技术,不只能够实现Java访问C函数,也能够实现C语言调用Java代码。
而JNA只能实现Java访问C函数,做为一个Java框架,天然不能实现C语言调用Java代码。此时,你仍是须要使用JNI技术。
JNI是JNA的基础,是Java和C互操做的技术基础。有时候,你必须回归到基础上来。
(1)JNA—JNI终结者
(2)C++DLL编程详解