[TOC]
文章参考:https://glumes.com/post/android/android-jni-array-operation/
概述
在JNI中,java的基本数据类型可以直接与jni基本类型映射,但数组作为引用类型不能直接使用和修改,JNI提供了一组访问和处理数组的API。
创建数组
使用New<Type>Array
函数创建一个数组实例,其中Type为基本数据类型:Boolean、Byte、Char、Short、Int、Long、Float、Double,如NewIntArray
。
1 2 3 4
| jintArray array = env->NewIntArray(4); if (0 != array) { }
|
基本数据类型数组
JNI 中有两种数组操作,基础数据类型数组和对象数组,JNI 对待基础数据类型数组和对象数组是不一样的。
对于基本数据类型数组,JNI 都有和 Java 相对应的结构,在使用起来和基本数据类型的使用类似。
在 Android JNI 基础知识篇提到了 Java 数组类型对应的 JNI 数组类型。比如,Java int 数组对应了 jintArray,boolean 数组对应了 jbooleanArray。
如同 String 的操作一样,JNI 提供了对应的转换函数:GetArrayElements、ReleaseArrayElements。
1 2
| intArray = env->GetIntArrayElements(intArray_, NULL); env->ReleaseIntArrayElements(intArray_, intArray, 0);
|
另外,JNI 还提供了如下的函数:
1 2
| GetTypeArrayRegion / SetTypeArrayRegion
|
1 2
| GetPrimitiveArrayCritical / ReleasePrimitiveArrayCritical
|
代码示例
实际操作如下:
1 2
| private native int intArraySum(int[] intArray, int size);
|
对应的 C++ 代码如下:
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 30 31 32
| JNIEXPORT jint JNICALL Java_com_glumes_cppso_jnioperations_ArrayTypeOps_intArraySum(JNIEnv *env, jobject instance, jintArray intArray_, jint num) { jint *intArray; int sum = 0; intArray = env->GetIntArrayElements(intArray_, NULL); if (intArray == NULL) { return 0; } int length = env->GetArrayLength(intArray_); LOGD("array length is %d", length); for (int i = 0; i < length; ++i) { sum += intArray[i]; } LOGD("sum is %d", sum);
jint buf[num]; env->GetIntArrayRegion(intArray_, 0, num, buf); sum = 0; for (int i = 0; i < num; ++i) { sum += buf[i]; } LOGD("sum is %d", sum); env->ReleaseIntArrayElements(intArray_, intArray, 0); return sum; }
|
假如需要从 JNI 中返回一个基础数据类型的数组,对应的代码如下:
1 2
| private native int[] getIntArray(int num);
|
对应的 C++ 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
extern "C" JNIEXPORT jintArray JNICALL Java_com_glumes_cppso_jnioperations_ArrayTypeOps_getIntArray(JNIEnv *env, jobject instance, jint num) { jintArray intArray; intArray = env->NewIntArray(num);
jint buf[num]; for (int i = 0; i < num; ++i) { buf[i] = i * 2; }
env->SetIntArrayRegion(intArray, 0, num, buf); return intArray; }
|
以上例子,基本把相关的操作都使用上了,可以发现和 String 的操作大都是相似的。
对象数组
对于对象数组,也就是引用类型数组,数组中的每个类型都是引用类型,JNI 只提供了如下函数来操作。
1
| GetObjectArrayElement / SetObjectArrayElement
|
和基本数据类型不同的是,不能一次得到数据中的所有对象元素或者一次复制多个对象元素到缓冲区。只能通过上面的函数来访问或者修改指定位置的元素内容。
字符串和数组都是引用类型,因此也只能通过上面的方法来访问。
例如在 JNI 中创建一个二维的整型数组并返回:
1 2
| private native int[][] getTwoDimensionalArray(int size);
|
二维数组具有特殊性在于,可以将它看成一维数组,其中数组的每项内容又是一维数组。
具体 C++ 代码如下:
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 30 31 32 33 34 35 36 37 38 39 40 41 42
|
extern "C" JNIEXPORT jobjectArray JNICALL Java_com_glumes_cppso_jnioperations_ArrayTypeOps_getTwoDimensionalArray(JNIEnv *env, jobject instance, jint size) { jobjectArray result; jclass intArrayCls = env->FindClass("[I");
if (intArrayCls == NULL) { return NULL; } result = env->NewObjectArray(size, intArrayCls, NULL);
if (result == NULL) { return NULL; } for (int i = 0; i < size; ++i) { jint tmp[256]; jintArray iarr = env->NewIntArray(size); if (iarr == NULL) { return NULL; } for (int j = 0; j < size; ++j) { tmp[j] = i + j; } env->SetIntArrayRegion(iarr, 0, size, tmp); env->SetObjectArrayElement(result, i, iarr); env->DeleteLocalRef(iarr); } return result; }
|
首先需要使用 NewObjectArray 方法来创建对象数组。
然后使用 SetObjectArrayElement 函数填充数据时,需要构建好每个位置对应的对象。这里就使用了 NewIntArray 来创造了一个对象,并给对象填充数据后,在赋值给对象数组。
通过一个 for 循环就完成给对象数组赋值的操作。
在创建对象数组时,有一个操作是找到对应的对象类型,通过 findClass 方法。findClass 的参数 [I 这里就涉及到 Java 与 JNI 对应签名的转换。