JNA 之 初识(上)

      JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。使用JNI调用共享类库(.dll/.so文件)是非常麻烦的事情,既需要编写java代码,又要编写C语言的代理方法,这其中需要很多数据类型的转换,是让人非常头痛。JNA框架就是为了解决这些问题和繁琐的事情而开发的,它提供一组Java工具类用于在运行期动态访问系统本地共享类库而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射,大大降低了Java调用本体共享库的开发难度。JNA与.NET平台上的P/Invoke机制一样简单和方便。

      你只需要下载一个jar包,就可以使用JNA的强大功能方便地调用动态链接库中的C函数。下载地址是:https://github.com/twall/jna

      JNA调用本地的库函数

    假设有一个动态链接库: CnblogsJna.dll。里面有这样一个函数:

void sayHello(char * name){
    printf("C Code Start...\n");
    printf("Hello! Mr %s.\n",name);
    printf("C Code End.\n");
}

      此函数接收一个代表姓名的字符指针,然后在控制台上输出几句字符串。

      为了调用这个函数,使用JNA,我们需要编写下面的JAVA代码:

      1、接口ICnblogsJna.java

import com.sun.jna.Library;
import com.sun.jna.Native;

/**
 * @author BCH)王国成
 */
public interface ICnblogsJna extends Library {
    // 接口实例
    ICnblogsJna INSTANCE = (ICnblogsJna) Native.loadLibrary("CnblogsJna",ICnblogsJna.class);

    // 与C代码映射的函数
    public void sayHello(String name);
}

      注意:接口需要继承制JNA的Library接口;

             接口内部需要一个公共静态常量INSTANCE, 通过这个常量,就可以获得这个接口的实例,从而使用接口的方法。也就是调用动态链接库CnblogsJna.dll中的sayHello函数了。
            如果使用JNI,你需要使用System.loadLibrary方法,来加载我们专为JNI编写的动态链接库,这个动态链接库实际上是我们真正需要的动态链接库的代理。使用JNA是,需要用JNA类库的Native类的loadLibrary函数,是直接把我们需要的动态链接库载入进来。使用JNA,我们不需要编写作为代理的动态链接库,不需要编写一行原生代码。
            Native类的loadLibrary方法有两个参数:第一个参数是.dll或者.so文件的名字,但不带后缀名。这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。第二个参数是本接口的Class类型,JNA通过这个Class类型,根据指定的dll/.so文件,动态创建接口的实例。

      2、调用动态链接库文件中函数的Java方法():

/**
 * @author BCH)王国成
 */
public class CnblogsJna {
    /**
     * 入口函数
     * @param args
     */
    public static void main(String[] args) {
        // 调用动态链库中的sayHello函数
        ICnblogsJna.INSTANCE.sayHello("Wanggc/王国成");
    }
}

      方法很简单,就像调用Java自己的函数一样。执行输出结果如下:

     

      和原生代码的类型映射

      跨平台,跨语言调用的难点,就是不同语言之间数据类型不一致造成的,JNA也不例外,要想跨平台调用,数据类型转换是个无法回避的问题。JNA提供了Java和原生代码的类型映射。

      Java和C数据类型的对应表如下:

Java

C

原生表

 boolean

 int

 32位整数 (可定制)

 byte

 char 

 8位整数

 char

 wchar_t

 平台依赖

 short

 short

 16位整数

 int

 int

 32位整数

 long

long long, __int64

 64位整数

 float

 float

 32位浮点数

 double

 double

 64位浮点数

 Buffer/Pointer

 pointer

 平台依赖(32或 64位指针)

 <T>[] (基本类型的数组)

 pointer/array

32或 64位指针(参数/返回值)

邻接内存(结构体成员)

 String

 char*

/0结束的数组 (native encoding or jna.encoding)

 WString

 wchar_t*

 /0结束的数组(unicode)

 String[]

 char**

 /0结束的数组的数组

 WString[]

 wchar_t**

 /0结束的宽字符数组的数组

 Structure

 struct*/struct

指向结构体的指针 (参数或返回值) (或者明确指定是结构体指针)
结构体(结构体的成员) (或者明确指定是结构体)

 Union

union 

 等同于结构体

 Structure[]

 struct[]

 结构体的数组,邻接内存

 Callback

 <T> (*fp)()

 Java函数指针或原生函数指针

 NativeMapped

 varies

 依赖于定义

 NativeLong

 long

 平台依赖(32或64位整数)

 PointerType

 pointer

 和 Pointer相同

 

      由于跨平台和跨语言尤其自身无法克服的确定,所以尽量少跨平台、跨语言传递数据。如果必须这样做,也尽量使用简单的数据类型。如果有复杂的数据类型需要在Java和原生函数中传递,那么我们就必须在Java中模拟这种复杂的原生类型。这将大大增加实现的难度,甚至无法实现。如果在Java和原生函数间存在大量的数据传递,一方面,会损失程序的性能;另一方面会造成内存碎片,Java调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这些Java数据。这些数据,JVM的GC不能管理,就会造成内存碎片。

posted @ 2012-06-29 10:09  孤旅者  阅读(11811)  评论(0编辑  收藏  举报