JNI开发

来源:互联网 发布:学校网络管理制度 编辑:程序博客网 时间:2024/06/10 09:55

 JNI是Java Native Interface的缩写。从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的。当然也可以与其他语言交互,只要调用约定受支持就可以了。
    但是这样做丧失了平台可移植性,但是有些时候我们这样做比较方便,比如以前我们项目组做网管软件,需要很多获取网络中的硬件信息,当然底层使用c语言进行信息扫描,一开始我们采用socket通信,实现c与java通信;但是随着数据信息的增加,socket通信任务就繁重起来,因此我们不得不自定义协议来实现socket下的复杂类型的数据通信。最后我们采用JNI非常方便的就完成了c与java的通信。主要是java调用c语言的方法。
这几天有学生问关于JNI的问题,那么就以实例 做一些简单介绍吧。

先说一下JNI开发的步骤:

  • 编写带有native声明的方法的java类
  • 使用javac命令编译所编写的java类
  • 使用javah java类名 生成扩展名为h的头文件
  • 使用C/C++实现本地方法
  • 将C/C++编写的文件生成动态连接库
  • 调用本地方法OK

1.java类:TestJNI.java

public class TestJNI {
     static {
      try {
           System.loadLibrary("hello");
      } catch (Exception ex) {
           ex.printStackTrace();
     }
    }
   //求平方
   private native int square(int i);


   public static void main(String[] args)
  {
     TestJNI ht=new TestJNI ();
     int result=ht.square(4);

           //下面输出应该是9
           System.out.println("the result is :" + result);
       }
    }

这一步应该都没有问题,但是需要解释两个地方:
   1).声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为native的,并且不能实现。该方法由其他语言进行实现。一般是用c语言实现。那么就需要进行java的数据类型与c语言的数据类型映射的问题,比如:private native int square(int i);参数与返回值的数据类型都是int,那么c语言实现该方法时对应的数据类型是什么呢?String,float等等又该如何进行映射呢?

java类型c语言类型说明booleanjboolean无符号 8位bytejbyte无符号 8位charjchar无符号,16 位shortjshort有符号,16 位intjint有符号,32 位longjlong有符号,64 位floatjfloat32位doublejdouble64位voidvoid 

还要引用数据类型:

jobject
  |--jclass

  |--jstring

  |--jarray

       |--jobjectArray

       |--jbooleanArray

       |--jbyteArray

       |--jcharArray

       |--jshortArray

       |--jintArray

       |--jlongArray

       |--jfloatArray

       |--jdoubleArray

当然,还有其他的数据类型没有列举。

 

2).System.loadLibrary("hello"):就是加载名称是hello的动态链接库,如果是windows下则为dll文件,如果是linux下则是so文件,系统根据jdk自动判断平台。一般该文件放在系统环境变量path中,因此我们可以吧动态链接库文件放在%JAVA_HOME%/bin目录下即可。jdk中该方法相当于:

   Runtime.getRuntime().loadLibrary(String);

当然与该方法相似的是System.load(String fileName) 和Runtime.getRuntime().load(String fileName)。程序本地方法调用前应该先加载native方法的本地实现(就是实现本地方法的动态链接库),因此该方法一般放在静态块中。

2.编译

   执行javac TestJNI.java 如果没有报错则编译成为TestJNI.class文件,这一步没有什么问题。

3.生成头文件

   执行javah HelloTest 会生成一个文件HelloTest.h文件,内容大致如下:


#include <jni.h>

#ifndef _Included_HelloTest
#define _Included_HelloTest
#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jint JNICALL Java_TestJNI_square
(JNIEnv *, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif

 关键是

JNIEXPORT jint JNICALL Java_TestJNI_square (JNIEnv *, jobject, jint);

   该方法是从java类生成供c语言实现的方法,该文件尽量不要修改。这里的数据类型就是上面的提到的映射后的数据类型,该数据类型是在

   %JAVA_HOME%/include/jni.h中定义,

4.实现该方法。hello.c

#include "TestJNI.h"

JNIEXPORT jint JNICALL Java_TestJNI_square(JNIEnv *env, jobject obj, jint r)
{
    return r*r;
}

这一步也应该没有什么问题。接下来就是要生成动态连接库了。

 

5.生成动态链接库文件.

    生成动态连接库文件,就是编译该c文件输出为动态连接库,如果是windows则生成dl文件如果是linux则生成so文件。不同的平台c语言的编译器各不相同,现在我在windows平台下使用vc的编译器进行编译(只要安装了vc++6.0就可以了)关于vc编译器CL的用法,大家可以google一下,非常麻烦的。

cl -I%java_home%/include -I%java_home%/include/win32 -LD hello.c -Fehello.dll

    生成的dll文件名在选项-Fe后面配置,这里是hello,因为在HelloTest.java文件中我们loadLibary的时候使用的名字是hello。两个地方要相同。另外需要将-I%java_home%/include -I%java_home%/include/win32参数加上。

生成hello.dll文件之后拷贝hello.dll到%java_home%/bin目录下即可使用,最好吧HelloTest.h文件拷贝到%java_home%/include目录下。

6.调用该方法执行main函数即可运行成功:

java TestJNI命令运行测试控制台输出结果是16,运行成功。

原创粉丝点击