美文网首页
java调用本地方法--JNI访问对象型数组

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

作者: w1992wishes | 来源:发表于2018-05-11 16:25 被阅读74次

    本篇结构:

    • 简介
    • 实例

    一、简介

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

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

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

    • GetObjectArrayElement / SetObjectArrayElement
      和基本数据类型不同的是,不能一次得到数据中的所有对象元素或者一次复制多个对象元素到缓冲区。只能通过上面的函数来访问或者修改指定位置的元素内容。

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

    二、实例

    2.1、编写Java类

    public class ObjectArray {
        private native int[][] initInt2DArray(int size);
    
        public static void main(String[] args) {
            ObjectArray obj = new ObjectArray();
            int[][] arr = obj.initInt2DArray(3);
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    System.out.format("arr[%d][%d] = %d\n", i, j, arr[i][j]);
                }
            }
        }
    
        static {
            System.loadLibrary("ObjectArray");
        }
    }
    

    2.2、编译java类

    javac ObjectArray.java

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

    javah -d jnilib -jni ObjectArray

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class ObjectArray */
    
    #ifndef _Included_ObjectArray
    #define _Included_ObjectArray
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     ObjectArray
     * Method:    initInt2DArray
     * Signature: (I)[[I
     */
    JNIEXPORT jobjectArray JNICALL Java_ObjectArray_initInt2DArray
      (JNIEnv *, jobject, jint);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

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

    #include "ObjectArray.h"
    
    JNIEXPORT jobjectArray JNICALL Java_ObjectArray_initInt2DArray
      (JNIEnv *env, jobject obj, jint size)
    {
        jobjectArray result;
        jclass clsIntArray;
        jint i,j;
        // 1.找到对象数组中具体的对象类型,[I 指的就是数组类型
        clsIntArray = (*env)->FindClass(env,"[I");
        if (clsIntArray == NULL)
        {
            return NULL;
        }
        // 2.创建一个数组对象(里面每个元素用clsIntArray表示,都是数组类型)
        result = (*env)->NewObjectArray(env,size,clsIntArray,NULL);
        if (result == NULL)
        {
            return NULL;
        }
    
        // 3.为数组元素赋值
        for (i = 0; i < size; ++i)
        {
            jint buff[256];
            jintArray intArr = (*env)->NewIntArray(env,size);
            if (intArr == NULL)
            {
                return NULL;
            }
            for (j = 0; j < size; j++)
            {
                buff[j] = i + j;
            }
            (*env)->SetIntArrayRegion(env,intArr, 0,size,buff);
            (*env)->SetObjectArrayElement(env,result, i, intArr);
            (*env)->DeleteLocalRef(env,intArr);
        }
    
        return result;
    }
    

    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就会挂掉。

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/qdofdftx.html