目录
上一篇 深入浅出Android NDK之JNI字符串转换
在学习本章内容之前,建议你先学习上一章的内容。
深入浅出Android NDK之JNI字符串转换
因为有很多相似概念,所以本章不会重新讲解。
和字符串一样,jni对数组类型也提供了特殊支持。提供了9种数组:
jbooleanArray,jbyteArray,jcharArray,jshortArray,jintArray,jlongArray,jfloatArray,jdoubleArray,jobjectArray
分别对应java中的:
boolean[],byte[],char[],short[],int[],long[],float[],double[],Object[]。
jobjectArray可以对应java中的一切类对像数组,比如,String[],Integer[],InputStream[],File[]等等。
对于数组JNIEnv中共提供了以下API进行支持:
jsize GetArrayLength(jarray array);
jobjectArray NewObjectArray(jsize length, jclass elementClass,jobject initialElement);
jobject GetObjectArrayElement(jobjectArray array, jsize index);
void SetObjectArrayElement(jobjectArray array, jsize index, jobject value);
jbooleanArray NewBooleanArray(jsize length);
jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy);
void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems, jint mode);
void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf);
void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean* buf);
jbyteArray NewByteArray(jsize length);
jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy);
void ReleaseByteArrayElements(jbyteArray array, jbyte* elems, jint mode);
void GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf);
void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, const jbyte* buf);
jcharArray NewCharArray(jsize length);
jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy);
void ReleaseCharArrayElements(jcharArray array, jchar* elems, jint mode);
void GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* buf);
void SetCharArrayRegion(jcharArray array, jsize start, jsize len, const jchar* buf);
jshortArray NewShortArray(jsize length);
jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy);
void ReleaseShortArrayElements(jshortArray array, jshort* elems, jint mode);
void GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf);
void SetShortArrayRegion(jshortArray array, jsize start, jsize len, const jshort* buf);
jintArray NewIntArray(jsize length);
jint* GetIntArrayElements(jintArray array, jboolean* isCopy);
void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode);
void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf);
void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint* buf);
jlongArray NewLongArray(jsize length);
jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy);
void ReleaseLongArrayElements(jlongArray array, jlong* elems, jint mode);
void GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf);
void SetLongArrayRegion(jlongArray array, jsize start, jsize len, const jlong* buf);
jfloatArray NewFloatArray(jsize length);
jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy);
void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems, jint mode);
void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf);
void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, const jfloat* buf);
jdoubleArray NewDoubleArray(jsize length);
jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy);
void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems, jint mode);
void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf);
void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble* buf);
void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy);
void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode);
我们可以看到对于基本类型的数组提供的函数基本都是一样的,以jintArray为例:
jintArray NewIntArray(jsize length);
jint* GetIntArrayElements(jintArray array, jboolean* isCopy);
void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode);
void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf);
void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint* buf);
void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy);
void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode);
和上一章操作String的API进行比较:
jstring NewString(const jchar* unicodeChars, jsize len);
const jchar* GetStringChars(jstring string, jboolean* isCopy);
void ReleaseStringChars(jstring string, const jchar* chars);
void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf);
const jchar* GetStringCritical(jstring string, jboolean* isCopy);
void ReleaseStringCritical(jstring string, const jchar* carray);
也大同小异,只是多了修改数组的操作:
void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint* buf);
对于String的Get系统函数返回值是const jchar*,而对于jintArray的Get系统函数返回值是jint*,区别在const,因为字符串是不可变的,所以返回值是const。
对于数组的Get系列,isCopy参数的意义和Strinng的是一样的。
对于Release系列函数和String相比,多了一个jint mode的参数。
我们知道我们调用Get系列函数时,得于的C语言的数组指针有可能是一个拷贝,也有就是原始的引用。如果是原始引用,那mode参数基本上没什么作用了,传什么值 过去都可以。
在是拷贝的情况下,假设有以下代码:
jboolean isCopy;
jint *elem = env->GetIntArrayElements(javaArray,&isCopy);
假设isCopy的返回值是true,也就是elem是一份拷贝。
如果我们得到elem的值后,我们修改了elem的值,这个时候mode参数我们入当传入0。
0的意思是,将elem拷贝到原始数组中去,并且释放elem。
elem[0] = 1;
env->ReleaseIntArrayElements(javaArray, elem, 0);
如果我们得到elem的值后,我们并不修改,仅仅只用来读取。这种情况下我们应当传入JNI_ABORT。JNI_ABORT会释放elem,但并不会将elem的内容拷贝到原始数组,因为我们并没有修改,所以就没必要再次拷贝了。
env->ReleaseIntArrayElements(javaArray, elem, JNI_ABORT);
mode还有可以指定为JNI_COMMIT,JNI_COMMIT的意思是将elem的内容拷贝到原始数组,但是并不释放elem,所以你可以继续修改或者读取elem,等完事后,再次调用ReleaseIntArrayElements传入0或者JNI_ABORT来释放elem。不管怎么样,elem最终是一定要释放的。这种情况太少见了,所以我建议你忘掉JNI_COMMIT。我实在是想像不出应用场景。
下面我们来总结一下:
如果修改了数组内容,那么mode传0。
如果只读,那么mode传JNI_ABORT。
对于jobjectArray,并没有提供批量操作函数(GetObjectArrayElements,GetObjectRegion,SetObjectArrayRegion),所以我们只能用以下API,老老实实的一个一个读取赋值:
jobject GetObjectArrayElement(jobjectArray array, jsize index);
void SetObjectArrayElement(jobjectArray array, jsize index, jobject value);