JNI 访问Java数组
本文将介绍JNI如何访问基本类型数组、对象数组(包括字符串数组)以及二维数组。可以使用GetArrayLength来获取数组的长度,使用GetIntArrayElements等函数直接访问基本类型数组中的元素,使用GetObjectArrayElement等函数访问对象数组和字符串数组,使用过程中确保类型匹配。
1、引子
JNI 中的数组分为基本类型数组和对象数组,它们的处理方式是不一样的,基本类型数组中的所有元素都是 JNI 的基本数据类型,可以直接访问。而对象数组中的所有元素是一个类的实例或其它数组的引用,和字符串操作一样,不能直接访问 Java 传递给 JNI 层的数组,必须选择合适的 JNI 函数来访问和设置Java层的数组对象。
2、数组访问
2.1 基本类型数组
Java 层:
private native double[] sumAndAverage(int[] numbers);
JNI 层:
JNIEXPORT jdoubleArray JNICALL Java_HelloJNI_sumAndAverage(JNIEnv *env, jobject obj, jintArray inJNIArray) {//类型转换 jintArray -> jint* jboolean isCopy;jint* inArray = env->GetIntArrayElements(inJNIArray, &isCopy);if (JNI_TRUE == isCopy) {cout << "C 层的数组是 java 层数组的一份拷贝" << endl;} else {cout << "C 层的数组指向 java 层的数组" << endl;}if(nullptr == inArray) return nullptr;//获取到数组长度jsize length = env->GetArrayLength(inJNIArray);jint sum = 0;for(int i = 0; i < length; ++i) {sum += inArray[i];}jdouble average = (jdouble)sum / length;//释放数组env->ReleaseIntArrayElements(inJNIArray, inArray, 0); // release resource//构造返回数据,outArray 是指针类型,需要 free 或者 delete 吗?要的jdouble outArray[] = {sum, average};jdoubleArray outJNIArray = env->NewDoubleArray(2);if(NULL == outJNIArray) return NULL;//向 jdoubleArray 写入数据env->SetDoubleArrayRegion(outJNIArray, 0, 2, outArray);return outJNIArray; }
2.2 引用类型数组
Java 层:
public native String[] operateStringArrray(String[] array);
JNI 层:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
JNIEXPORT jobjectArray JNICALL Java_com_xxx_jni_JNIArrayManager_operateStringArrray (JNIEnv * env, jobject object, jobjectArray objectArray_in){ //获取到长度信息 jsize size = env->GetArrayLength(objectArray_in); /*******获取从JNI传过来的String数组数据**********/ for(int i = 0; i < size; i++) { jstring string_in= (jstring)env->GetObjectArrayElement(objectArray_in, i); char *char_in = env->GetStringUTFChars(str, nullptr); } /***********从JNI返回String数组给Java层**************/ jclass clazz = env->FindClass("java/lang/String"); jobjectArray objectArray_out; const int len_out = 5; objectArray_out = env->NewObjectArray(len_out, clazz, NULL); char * char_out[]= { "Hello,", "world!", "JNI", "is", "fun" }; jstring temp_string; for( int i= 0; i < len_out; i++ ) { temp_string = env->NewStringUTF(char_out[i]); env->SetObjectArrayElement(objectArray_out, i, temp_string); }<br> return objectArray_out;} |
对于对象数组,也就是引用类型数组,数组中的每个类型都是引用类型,JNI 只提供了如下函数来操作:
- GetObjectArrayElement
- SetObjectArrayElement
和基本数据类型不同的是,不能一次得到数据中的所有对象元素或者一次复制多个对象元素到缓冲区。只能通过上面的函数来访问或者修改指定位置的元素内容。
2.3 二维数组
Java 层:
public native int[][] operateTwoIntDimArray(int[][] array_in);
JNI 层:
JNIEXPORT jobjectArray JNICALL Java_com_xxx_jni_JNIArrayManager_operateTwoIntDimArray(JNIEnv * env, jobject object, jobjectArray objectArray_in) {/********** 解析从Java得到的int型二维数组 **********/int i, j ;const int row = env->GetArrayLength(objectArray_in);//获取二维数组的行数jarray array = (jarray)env->GetObjectArrayElement(objectArray_in, 0);const int col = env->GetArrayLength(array);//获取二维数组每行的列数//根据行数和列数创建int型二维数组 jint intDimArrayIn[row][col];for(i =0; i < row; i++){
array = (jintArray)env->GetObjectArrayElement(objectArray_in, i);//操作方式一,这种方法会申请natvie memory内存jint *coldata = env->GetIntArrayElements((jintArray)array, NULL ); for (j=0; j<col; j++) { intDimArrayIn [i] [j] = coldata[j]; //取出JAVA类中int二维数组的数据,并赋值给JNI中的数组 } //操作方式二,赋值,这种方法不会申请内存// env->GetIntArrayRegion((jintArray)array, 0, col, (jint*)&intDimArrayIn[i]);
env->ReleaseIntArrayElements((jintArray)array, coldata,0 ); }/**************创建一个int型二维数组返回给Java**************/const int row_out = 2;//行数const int col_out = 2;//列数//获取数组的classjclass clazz = env->FindClass("[I");//一维数组的类//新建object数组,里面是int[]jobjectArray intDimArrayOut = env->NewObjectArray(row_out, clazz, NULL);int tmp_array[row_out][col_out] = {{0,1}, {2,3}};for(i = 0; i< row_out; i ++){jintArray intArray = env->NewIntArray(col_out);env->SetIntArrayRegion(intArray, 0, col_out, (jint*)&tmp_array[i]);env->SetObjectArrayElement(intDimArrayOut, i, intArray);}
return intDimArrayOut; }
3、小结
GetArrayLength :返回数组中的元素个数
jsize (GetArrayLength)(JNIEnv env, jarray array);
NewObjectArray :构建 JNI 引用类型的数组,它将保存类 elementClass 中的对象。所有元素初始值均设为 initialElement,一般使用 NULL 就好。如果系统内存不足,则抛出 OutOfMemoryError 异常
jobjectArray NewObjectArray (JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
GetObjectArrayElement :返回 jobjectArray 数组的元素,通常是获取 JNI 引用类型数组元素。如果 index 不是数组中的有效下标,则抛出 ArrayIndexOutOfBoundsException 异常
jobject GetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index);
SetObjectArrayElement :设置 jobjectArray 数组中 index 下标对象的值。如果 index 不是数组中的有效下标,则会抛出 ArrayIndexOutOfBoundsException 异常。如果 value 的类不是数组元素类的子类,则抛出 ArrayStoreException 异常。
void SetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index, jobject value);
New<PrimitiveType>Array 函数集:用于构造 JNI 基本类型数组对象。在实际应用中把 PrimitiveType 替换为某个实际的基本类型数据类型,然后再将 NativeType 替换成对应的 JNI Native Type 即可,具体的:
NativeTypeArray New<PrimitiveType>Array (JNIEnv* env, jsize size)
|
1
2
3
4
5
6
7
8
9
|
函数名 返回类型NewBooleanArray() jbooleanArrayNewByteArray() jbyteArrayNewCharArray() jcharArrayNewShortArray() jshorArrayNewIntArray() jintArrayNewLongArray() jlongArrayNewFloatArray() jfloatArrayNewDoubleArray() jdoubleArray |
Get<PrimitiveType>ArrayElements函数集:该函数用于将 JNI 数组类型转换为 JNI 基本数据类型数组,在实际使用过程中将 PrimitiveType 替换成某个实际的基本类型元素访问函数,然后再将NativeType替换成对应的 JNI Native Type 即可:
NativeType* Get<PrimitiveType>ArrayElements(JNIEnv *env, NativeTypeArray array, jboolean *isCopy)
函数名 转换前类型 转换后类型 GetBooleanArrayElements() jbooleanArray jboolean* GetByteArrayElements() jbyteArray jbyte* GetCharArrayElements() jcharArray jchar* GetShortArrayElements() jshortArray jshort* GetIntArrayElements() jintArray jint* GetLongArrayElements() jlongArray jlong* GetFloatArrayElements() jfloatArray jfloat* GetDoubleArrayElements() jdoubleArray jdouble*
Release<PrimitiveType>ArrayElements函数集:该函数用于通知 JVM,数组不再使用,可以清理先关内存了。在实际使用过程中将 PrimitiveType 替换成某个实际的基本类型元素访问函数,然后再将 NativeType 替换成对应的 JNI Native Type 即可:
void Release<PrimitiveType>ArrayElements (JNIEnv *env, NativeTypeArray array, NativeType *elems,jint mode);
函数名 NativeTypeArray NativeType
ReleaseBooleanArrayElements() jbooleanArray jboolean
ReleaseByteArrayElements() jbyteArray jbyte
ReleaseCharArrayElements() jcharArray jchar
ReleaseShortArrayElements() jshortArray jshort
ReleaseIntArrayElements() jintArray jint
ReleaseLongArrayElements() jlongArray jlong
ReleaseFloatArrayElements() jfloatArray jfloat
ReleaseDoubleArrayElements() jdoubleArray jdouble
Get/Set<PrimitiveType>ArrayRegion :该函数用于将基本类型数组某一区域复制到 JNI 数组类型中。在实际使用过程中将 PrimitiveType 替换成某个实际的基本类型元素访问函数,然后再将 NativeType 替换成对应的 JNI Native Type 即可:
void Set<PrimitiveType>ArrayRegion (JNIEnv *env, NativeTypeArray array, jsize start, jsize len, NativeType *buf);
函数名 NativeTypeArray NativeType
SetBooleanArrayRegion() jbooleanArray jboolean
SetByteArrayRegion() jbyteArray jbyte
SetCharArrayRegion() jcharArray jchar
SetShortArrayRegion() jshortArray jshort
SetIntArrayRegion() jintArray jint
SetLongArrayRegion() jlongArray jlong
SetFloatArrayRegion() jfloatArray jfloat
SetDoubleArrayRegion() jdoubleArray jdouble
参考自:https://blog.csdn.net/chuyouyinghe/article/details/131241999

