JNI应用之Windows篇

来源:互联网 发布:网络宣传方式 编辑:程序博客网 时间:2024/06/08 17:33

JNI技术简介

JNI:Java Native Interface

其基本原理为:JNI允许在Java虚拟机上运行的Java代码操作其他语言编写的应用程序和本地库方法,从而能够直与接特定的操作系统和硬件平台进行交互。JNI提供的编程接口也允许在本地应用程序中嵌入Java虚拟机。交互过程是在相同的Java虚拟机中使得Java类中的本地方法的调用能够被映射到共享二进制库的相对应的函数上,并在相同的进程空间加载。本地方法以库文件的形式存放,在Windows平台上是DLL文件形式,在Unix/Linux平台上是SO文件形式。Java虚拟机中内置了JNI,这样Java虚拟机能够激活本地系统调用完成客户机上的输入和输出以及图形、网络和线程操作。本地方法也能够调用Java方法,使用JNI,本地方法能够调用现有的Java方法,传递被调用的方法所需的参数,得到返回结果。

 

JNI的优缺点

优点:

1. 抛出和捕获本地方法产生的异常并交由Java程序处理;

2. 装载并获取Java类的信息。

3. 有效地避免了重复开发,使得软件中的科学计算模块能够采用效率较高的语言实现。

缺点:

1. 使用JNI使得Java失去了可移植性。如果想要移植到别的平台上,那么native代码就需要重新编写;

2. JVM给Java代码提供了完善的安全机制使得Java代码不会导致程序崩溃、滥用数据等,一旦使用了JNI,这种安全机制就无能力了;

3. 如果可以通过TCP/IP实现Java代码与本地C/C++代码的交互工作,那么最好不要使用JNI方式,因为一次JNI调用非常耗时,大概要花费0.5~1个毫秒;

4. 在一个applet的应用中,不要使用JNI,因为在applet中可能引发安全异常;

5. 本地代码运行时,没有有效地防暑组越界错误、错误指针引用带来的间接错误等,所以必须保证本地代码的稳定性,因为,丝毫的错误都可能导致Java虚拟机崩溃。

 

JNI适用场景

1. 标准的Java类库不支持所需的平台相关的特性;

2. 原有的使用其他语言编写的库或应用程序需要与Java应用程序交互;

3. 为提高程序性能,或者为了与操作系统、硬件交互,程序中有使用汇编等低级语言编写的部分,而Java应用程序需要调用它们。

 

使用JNI需要注意的细节

1. 在C/C++中,char是8bits,以空字符为结束标志,而在Java中,char是16bits,并作为Unicode字符存储的,因此在使用char类型的时候要做转换。;

2. C++可以通过引用返回多个值,而JNI则需要通过数组返回多个值。如果要返回基本类型或者String类型,则需要建立一维数组,该数组只包含一个元素。

3. JNI将二维数组作为一维对象数组进行处理。

4. 将所有本地方法都封装在单个类中,这个类调用单个DLL。对于每种目标操作系统,都可以用特定于适当平台的版本替换这个DLL。这样就可以将本地代码的影响减至最小,并有助于将以后所需的移植问题包含在内。

 

使用JNI的步骤

1. 首先在Java类中声明一个native的方法;

2. 使用javah(/bin)命令生成包含native方法声明的C/C++头文件;

3. 按照生成的C/C++头文件来编写C/C++源文件;

4. 将C/C++源文件编译生成动态链接库dll或者so;

5. 如果想要使得每次更新库文件后立即生效,可以将该库文件的路径添加到PATH环境变量中;

6. Java类中加载库文件(DLL/so),然后调用声明的native方法。

注:dll文件与exe一样是可执行的二进制代码

 

示例:

第一步:编写Java端代码

使用javac 编译生成class文件;

 

第二步:使用 javah 编译生成头文件:TestNative.h,分析一下 TestNative.h,生成的代码如下:

参数解释:

JNIEnv:在该方法中, 所有的JNI调用都使用了JNIEnv*类型的指针,习惯上在那个在CPP文件中将这个变量定义为env,它是任意的一个本地方法中的第一个参数。env指针指向的一个函数指针表,在VC中可以直接使用“->”操作符访问其中的函数。

jobject:指向在此Java代码中实例化的Java对象LocalFunction的一个句柄,相当于this指针。

后续的参数:就是本地调用中有Java程序传进的参数,由于本例的print函数没有任何参数,所以在TestNative.h头文件中的jobject之后没有跟其他参数。

 

第三步:建立一个dll工程,编写C代码

C代码中只需要完善在TestNative.h中声明好的函数即可,函数的结构与TestNative.h完全相同,为函数添加参数名env与obj。

此时如果直接编译,会提示找不到jni.h,将jdk安装目录\include\jni.h拷贝到当前c文件所在目录,然后将TestNative.h中的#include<jni.h>改为#include "jni.h",再次编译,此时又会提示没有jni_md.h,将jdk安装目录\include\win32\jni_md.h拷贝到当前路径下,编译、链接,生成一个dll文件。

 
第四步:进一步完善java代码,调用动态链接库

接着完善第一步中的java代码

注意,在代码中,System.loadLibrary("test")这一句是加载第三步中生成好的dll,test是该动态链接库的名字。注意参数中不需要加dll后缀。

运行顺利的话,将会看到久违的Hello World了。

 

遇到的问题小结:

1. 描述:

运行环境:Win7(64bit) + VS2010 + JDK6(64bit)   生成的dll大小为27kb

在运行的时候提示如下异常

Exception in thread "main" java.lang.UnsatisfiedLinkError: D:\Program_Files\Java
\acm\acm.dll: Can’t load IA 32-bit .dll on a AMD 64-bit platform
        at java.lang.ClassLoader$NativeLibrary.load(Native Method)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1803)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1728)
        at java.lang.Runtime.loadLibrary0(Runtime.java:823)
        at java.lang.System.loadLibrary(System.java:1028)
        at TestNative.main(TestNative.java:5)

最初认为是由于操作系统为64位,不兼容所导致的问题,所以改为如下运行环境:

XP(32bit) + VC++6.0 + JDK6(32bit)  生成的dll大小为220kb

在XP下运行一切正常。

若将XP下生成的dll拷贝到Win7(64bit)下运行,错误依然如上,如此可以初步推断,两个dll均是由于Win7(64bit)版本问题导致,那么为什么在Win7(64bit)下生成的dll也不适用于Win7呢?虽然安装的时候JDK为64位,但是位于\jdk1.6.0_21\include\中只有win32目录,在编译生成dll的时候,使用了该文件夹下的头文件,因此生成的dll也不可能是适用于64位系统的。

因此鉴于JDK目前对于JNI的支持仅有32位,所以在使用JNI的时候还是在32位下进行。

2. 如果不想将dll直接拷贝至当前路径,则可以通过配置环境变量来方便dll的修改,如果在运行的时候提示如下异常,则表示没有找到dll所在的位置。

Exception in thread "main" java.lang.UnsatisfiedLinkError: no acm in java.library.path
        at java.lang.ClassLoader.loadLibrary(Unknown Source)
        at java.lang.Runtime.loadLibrary0(Unknown Source)
        at java.lang.System.loadLibrary(Unknown Source)
        at TestNative.main(TestNative.java:5)

原创粉丝点击