您的位置:首页 > Web前端

JNI实例浅谈

2009-06-18 11:34 92 查看
JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。
  JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式,在LNUIX机器上是ELF文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。因为Java的编译运行都是在JVM,而它在操作系统上实际就是一个进程,因此我们只需要把C或C++的数据通过DLL,SO,ELF加载到该进程中,JVM就可实现对该数据的调用。

JNI(Java Native Interface)的书写步骤

     ·编写带有native声明的方法的java类

     ·使用javac命令编译所编写的java类

     ·使用javah  java类名生成扩展名为h的头文件

     ·使用C/C++实现本地方法

     ·将C/C++编写的文件生成动态连接库

·把.Dll的文件放到ClassPath路径下,Java就可以实现对其数据的调用

简单介绍在WINDOS的操作如下:

一、JAVA中所需要做的工作
  在JAVA程序中,首先需要在类中声明所调用的库名称,如下:
  static {
  System.loadLibrary(“TextDll”);
  }

  在这里,库的扩展名字可以不用写出来,由系统自己判断后会把格式自动加上去。
  还需要对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具 体实现。如下:
  public native static void set(String name);
  public native static String get();
  然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。
  例如程序TextDll.java,内容为:
  public class TextDll
  {
  static
  {
  System.loadLibrary(“TextDll”);
  }
  public native static String get();
  public native static void set(String name);
  public static void main(String[] args)
  {
  TextDll test = new TextDll l();
  test.set(“bluesky”);
  System.out.println(test.get());
  }
  }

  用javac TextDll .java编译它,会生成TextDll .class。
  再用javah TextDll,则会在当前目录下生成TextDll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。

二、C/C++中所需要做的工作
  对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。
  接上例子。我们先看一下TextDll.h文件的内容:
  
  #include
  
  #ifndef _Included_TextDl
  #define _Included_TextDl
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  JNIEXPORT jstring JNICALL Java_TextDll_get (JNIEnv *, jclass);
  
  JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jstring);
  #ifdef __cplusplus
  }
  #endif
  #endif
  在具体实现的时候,我们只关心两个函数原型
  JNIEXPORT jchar JNICALL Java_ TextDll _get (JNIEnv *, jclass); 和
  JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jstring);
  这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jstring是以JNI为中介使JAVA的String类型与本地的char沟通的一种类型,我们可以视而不见,就当做String使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。
  好,下面我们用TextDll.cpp文件具体实现这两个函数:
  #include "TextDll.h"
  char name[]={”bluesky”};

char* test;

JNIEXPORT jchar JNICALL Java_TextDll_get (JNIEnv *, jclass)
  {
  return “Text Success!”;

(*env)->ReleaseStringUTFChars(env, jstring, test);

  }
  JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jchar tName)
  {
   test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);

}
  编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是TextDll.dll 。把TextDll .dll拷贝到TextDll.class的目录下,javaTextDll运行它,就可以观察到结果了。

JNI中应注意的问题:

  1。 java和c是如何互通的?

     其实不能互通的原因主要是数据类型的问题,jni解决了这个问题,例如那个c文件中的jstring数据类型就是java传入的String对象,经过jni函数的转化就能成为c的char*。

对应数据类型关系如下表:

Java 类型 本地c类型 说明 boolean jboolean 无符号,8 位 byte jbyte 无符号,8 位 char jchar 无符号,16 位 short jshort 有符号,16 位 int jint 有符号,32 位 long jlong 有符号,64 位 float jfloat 32 位 double jdouble 64 位 void void N/A

   2. 如何将java传入的String参数转换为c的char*,然后使用?

 

java传入的String参数,在c文件中被jni转换为jstring的数据类型,在c文件中声明char* test,然后test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);注意:test使用完后,通知虚拟机平台相关代码无需再访问:(*env)->ReleaseStringUTFChars(env, jstring, test);

 

  3. 将c中获取的一个char*的buffer传递给java?

 

这个char*如果是一般的字符串的话,作为string传回去就可以了。如果是含有’/0’的buffer,最好作为bytearray传出,因为可以制定copy的length,如果copy到string,可能到’/0’就截断了。

有两种方式传递得到的数据:

     一种是在jni中直接new一个byte数组,然后调用函数(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);将buffer的值copy到bytearray中,函数直接return bytearray就可以了。

     一种是return错误号,数据作为参数传出,但是java的基本数据类型是传值,对象是传递的引用,所以将这个需要传出的byte数组用某个类包一下,如下:

 

class RetObj { public byte[] bytearray; } 这个对象作为函数的参数retobj传出,通过如下函数将retobj中的byte数组赋值便于传出。代码如下: jclass cls; jfieldID fid; jbyteArray bytearray; bytearray = (*env)->NewByteArr
4000
ay(env,len); (*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer); cls = (*env)->GetObjectClass(env, retobj); fid = (*env)->GetFieldID(env, cls, "retbytes", "[B"]); (*env)->SetObjectField(env, retobj, fid, bytearray);

4. 不知道占用多少空间的buffer,如何传递出去呢?

     在jni的c文件中new出空间,传递出去。java的数据不初始化,指向传递出去的空间即可。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jni java string buffer byte dll