[关闭]
@w1992wishes 2018-05-11T17:35:48.000000Z 字数 2717 阅读 1276

java调用本地方法--JNI访问对象型数组

JAVA_JNI


本篇结构:

一、简介

补充JNI对象数组访问实例。

JNI 中的数组分为基本类型数组和对象数组,它们的处理方式是不一样的,基本类型数组中的所有元素都是 JNI 的基本数据类型,可以直接访问。而对象数组中的所有元素是一个类的实例或其它数组的引用,和字符串操作一样,不能直接访问 Java 传递给 JNI 层的数组,必须选择合适的 JNI 函数来访问和设置 Java 层的数组对象。

对于对象数组,也就是引用类型数组,数组中的每个类型都是引用类型,JNI 只提供了如下函数来操作。

字符串和数组都是引用类型,因此也只能通过上面的方法来访问。

二、实例

2.1、编写Java类

  1. public class ObjectArray {
  2. private native int[][] initInt2DArray(int size);
  3. public static void main(String[] args) {
  4. ObjectArray obj = new ObjectArray();
  5. int[][] arr = obj.initInt2DArray(3);
  6. for (int i = 0; i < 3; i++) {
  7. for (int j = 0; j < 3; j++) {
  8. System.out.format("arr[%d][%d] = %d\n", i, j, arr[i][j]);
  9. }
  10. }
  11. }
  12. static {
  13. System.loadLibrary("ObjectArray");
  14. }
  15. }

2.2、编译java类

javac ObjectArray.java

2.3、生成相关JNI方法的头文件

javah -d jnilib -jni ObjectArray

  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class ObjectArray */
  4. #ifndef _Included_ObjectArray
  5. #define _Included_ObjectArray
  6. #ifdef __cplusplus
  7. extern "C" {
  8. #endif
  9. /*
  10. * Class: ObjectArray
  11. * Method: initInt2DArray
  12. * Signature: (I)[[I
  13. */
  14. JNIEXPORT jobjectArray JNICALL Java_ObjectArray_initInt2DArray
  15. (JNIEnv *, jobject, jint);
  16. #ifdef __cplusplus
  17. }
  18. #endif
  19. #endif

2.4、使用C/C++实现本地方法

  1. #include "ObjectArray.h"
  2. JNIEXPORT jobjectArray JNICALL Java_ObjectArray_initInt2DArray
  3. (JNIEnv *env, jobject obj, jint size)
  4. {
  5. jobjectArray result;
  6. jclass clsIntArray;
  7. jint i,j;
  8. // 1.找到对象数组中具体的对象类型,[I 指的就是数组类型
  9. clsIntArray = (*env)->FindClass(env,"[I");
  10. if (clsIntArray == NULL)
  11. {
  12. return NULL;
  13. }
  14. // 2.创建一个数组对象(里面每个元素用clsIntArray表示,都是数组类型)
  15. result = (*env)->NewObjectArray(env,size,clsIntArray,NULL);
  16. if (result == NULL)
  17. {
  18. return NULL;
  19. }
  20. // 3.为数组元素赋值
  21. for (i = 0; i < size; ++i)
  22. {
  23. jint buff[256];
  24. jintArray intArr = (*env)->NewIntArray(env,size);
  25. if (intArr == NULL)
  26. {
  27. return NULL;
  28. }
  29. for (j = 0; j < size; j++)
  30. {
  31. buff[j] = i + j;
  32. }
  33. (*env)->SetIntArrayRegion(env,intArr, 0,size,buff);
  34. (*env)->SetObjectArrayElement(env,result, i, intArr);
  35. (*env)->DeleteLocalRef(env,intArr);
  36. }
  37. return result;
  38. }

2.5、生成动态链接库

gcc -D_REENTRANT -fPIC -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -o libObjectArray.so ObjectArray.c

2.6、运行java

java -Djava.library.path=jnilib ObjectArray

2.7、解释

二维数组具有特殊性在于,可以将它看成一维数组,其中数组的每项内容又是一维数组。

本地函数initInt2DArray首先调用JNI函数FindClass获得一个int型的一维数组类的引用,传递给FindClass的参数"[I"是JNI class descript(NI类型描述符)。它对应着JVM中的int[]类型。如果int[]类加载失败的话,FindClass会返回NULL,然后抛出一个java.lang.NoClassDefFoundError: [I异常。

接下来,NewObjectArray创建一个新的数组,这个数组里面的元素类型用intArrCls(int[])类型来表示。然后使用 SetObjectArrayElement 函数填充数据时,需要构建好每个位置对应的对象。这里就使用了 NewIntArray 来创造了一个数组对象,并为每个数组元素分配空间,然后用SetIntArrayRegion把buff[]缓冲中的内容复制到新分配的一维数组中去。最后在外层循环中依次将int[]数组赋值到jobjectArray数组中,一维数组中套一维数组,就形成了一个所谓的二维数组。

另外,为了避免在循环内创建大量的JNI局部引用,造成JNI引用表溢出,所以在外层循环中每次都要调用DeleteLocalRef将新创建的jintArray引用从引用表中移除。在JNI中,只有jobject以及子类属于引用变量,会占用引用表的空间,jint,jfloat,jboolean等都是基本类型变量,不会占用引用表空间,即不需要释放。引用表最大空间为512个,如果超出这个范围,JVM就会挂掉。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注