JNI 编程上手指南之数组访问

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 层:

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);
    }
	return objectArray_out;
}

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;//列数

	//获取数组的class
	jclass 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. JNI 字符串处理函数

GetArrayLength

jsize (GetArrayLength)(JNIEnv env, jarray array);

返回数组中的元素个数

NewObjectArray

jobjectArray NewObjectArray (JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);

构建 JNI 引用类型的数组,它将保存类 elementClass 中的对象。所有元素初始值均设为 initialElement,一般使用 NULL 就好。如果系统内存不足,则抛出 OutOfMemoryError 异常

GetObjectArrayElement和SetObjectArrayElement

jobject GetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index)

返回 jobjectArray 数组的元素,通常是获取 JNI 引用类型数组元素。如果 index 不是数组中的有效下标,则抛出 ArrayIndexOutOfBoundsException 异常。

void SetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index, jobject value)

设置 jobjectArray 数组中 index 下标对象的值。如果 index 不是数组中的有效下标,则会抛出 ArrayIndexOutOfBoundsException 异常。如果 value 的类不是数组元素类的子类,则抛出 ArrayStoreException 异常。

New<PrimitiveType>Array 函数集

NativeTypeArray New<PrimitiveType>Array (JNIEnv* env, jsize size)

用于构造 JNI 基本类型数组对象。

在实际应用中把 PrimitiveType 替换为某个实际的基本类型数据类型,然后再将 NativeType 替换成对应的 JNI Native Type 即可,具体的:

函数名                      返回类型
NewBooleanArray()           jbooleanArray
NewByteArray()              jbyteArray
NewCharArray()              jcharArray
NewShortArray()             jshorArray
NewIntArray()               jintArray
NewLongArray()              jlongArray
NewFloatArray()             jfloatArray
NewDoubleArray()            jdoubleArray      

Get/ReleaseArrayElements函数集

NativeType* Get<PrimitiveType>ArrayElements(JNIEnv *env, NativeTypeArray array, jboolean *isCopy)

该函数用于将 JNI 数组类型转换为 JNI 基本数据类型数组,在实际使用过程中将 PrimitiveType 替换成某个实际的基本类型元素访问函数,然后再将NativeType替换成对应的 JNI Native Type 即可:

函数名                           转换前类型             转换后类型
GetBooleanArrayElements()       jbooleanArray          jboolean*
GetByteArrayElements()          jbyteArray             jbyte*
GetCharArrayElements()          jcharArray             jchar*
GetShortArrayElements()         jshortArray            jshort*
GetIntArrayElements()           jintArray              jint*
GetLongArrayElements()          jlongArray             jlong*
GetFloatArrayElements()         jfloatArray            jfloat*
GetDoubleArrayElements()        jdoubleArray           jdouble*
void Release<PrimitiveType>ArrayElements (JNIEnv *env, NativeTypeArray array, NativeType *elems,jint mode);

该函数用于通知 JVM,数组不再使用,可以清理先关内存了。在实际使用过程中将 PrimitiveType 替换成某个实际的基本类型元素访问函数,然后再将 NativeType 替换成对应的 JNI Native Type 即可:

函数名                              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

void Set<PrimitiveType>ArrayRegion (JNIEnv *env, NativeTypeArray array, jsize start, jsize len, NativeType *buf);

该函数用于将基本类型数组某一区域复制到 JNI 数组类型中。在实际使用过程中将 PrimitiveType 替换成某个实际的基本类型元素访问函数,然后再将 NativeType 替换成对应的 JNI Native Type 即可:

函数名                              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

关于

我叫阿豪,2015 年本科毕业于国防科技大学指挥自动化专业,毕业后,从事信息化装备的研发工作。主要研究方向为 Android Framework 与 Linux Kernel,2023年春节后开始做 Android Framework 相关的技术分享。

如果你对 Framework 感兴趣或者正在学习 Framework,可以参考我总结的Android Framework 学习路线指南,也可关注我的微信公众号,我会在公众号上持续分享我的经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY6SZi5v' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片