NDK-JNI数组数据处理
创始人
2024-06-02 22:23:56
0

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

0X00访问基本数据类型的数组

JNIEXPORT jbyteArray JNICALL Java_com_example_gnaix_ndk_NativeMethod_getByteArray(JNIEnv *env, jclass object, jbyteArray j_array){//1. 获取数组指针和长度jbyte *c_array = env->GetByteArrayElements(j_array, 0);int len_arr = env->GetArrayLength(j_array);//2. 具体处理jbyteArray c_result = env->NewByteArray(len_arr);jbyte buf[len_arr];for(int i=0; ibuf[i] = c_array[i] + 1;}//3. 释放内存env->ReleaseByteArrayElements(j_array, c_array, 0);//4. 赋值env->SetByteArrayRegion(c_result, 0, len_arr, buf);return c_result;
}

请添加图片描述
从 Java 层中传进去了一个数组,参数类型是 byte[], 对应 JNI 中 jbyteArray 类型。利用 GetByteArrayElements 函数获取数组指针,第二个参数返回的数组指针是原始数组,还是拷贝原始数据到临时缓冲区的指针,如果是 JNI_TRUE:表示临时缓冲区数组指针,JNI_FALSE:表示临时原始数组指针。开发当中,我们并不关心它从哪里返回的数组指针,这个参数填 NULL 即可,但在获取到的指针必须做校验。

类似的函数还有 GetIntArrayElements , GetFloatArrayElements , GetDoubleArrayElements 等。然后对数据进行了处理,并且释放了数组内存。ReleaseByteArrayElements 是与 GetByteArrayElements 对应使用的。

JNIEXPORT jint JNICALL Java_com_example_gnaix_ndk_NativeMethod_getByteArray(JNIEnv *env, jclass object, jbyteArray j_array){jbyte *c_array;jint len_arr;//1. 获取数组长度len_arr = env->GetArrayLength(j_array);//2. 申请缓冲区c_array = (jbyte *) malloc(sizeof(jbyte) * len_arr);//3. 初始化缓冲区memset(c_array, 0, sizeof(jbyte)*len_arr);//4. 拷贝java数组数据env->GetByteArrayRegion(j_array, 0, len_arr, c_array);//5. 计算总和int sum = 0;for(int i=0; isum += c_array[i];}//6. 释放缓冲区free(c_array);return sum;
}

0X01访问引用类型数组

JNI 提供了两个函数来访问对象数组,GetObjectArrayElement 返回数组中指定位置的元素,SetObjectArrayElement 修改数组中指定位置的元素。与基本类型不同的是,我们不能一次得到数据中的所有对象元素或者一次复制多个对象元素到缓冲区。因为字符串和数组都是引用类型,只能通过 Get/SetObjectArrayElement 这样的 JNI 函数来访问字符串数组或者数组中的数组元素。

/*** 名称: Person* 描述:** @author xiangqing.xue* @date 16/3/10*/
public class Person {private String TAG = "Person";public String name;public int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String toString(){String result =  "Name: " + name + ", age: " + age;Log.d(TAG, result);return result;}
}
****************************Native方法**********************************
JNIEXPORT jobjectArray JNICALL Java_com_example_gnaix_ndk_NativeMethod_getPersons(JNIEnv *env, jclass object){jclass clazz = NULL;jobject jobj = NULL;jmethodID mid_construct = NULL;jfieldID fid_age = NULL;jfieldID fid_name = NULL;jstring j_name;//1. 获取Person类的Class引用clazz = env->FindClass("com/example/gnaix/ndk/Person");if(clazz == NULL){LOGD("clazz null");return NULL;}//2. 获取类的默认构造函数IDmid_construct = env->GetMethodID(clazz, "", "()V");if(mid_construct == NULL){LOGD("construct null");return NULL;}//3. 获取实例方法ID和变量IDfid_name = env->GetFieldID(clazz, "name", "Ljava/lang/String;");fid_age = env->GetFieldID(clazz, "age", "I");if(fid_age==NULL || fid_name==NULL){LOGD("age|name null");return NULL;}//4. 处理单个对象并添加到数组int size = 5;jobjectArray obj_array = env->NewObjectArray(size, clazz, 0);for(int i=0; ijobj = env->NewObject(clazz, mid_construct);if(jobj == NULL){LOGD("jobject null");return NULL;}env->SetIntField(jobj, fid_age, 23 + i);j_name = env->NewStringUTF("gnaix");env->SetObjectField(jobj, fid_name, j_name);env->SetObjectArrayElement(obj_array, i, jobj);}//5. 释放局部变量env->DeleteLocalRef(clazz);env->DeleteLocalRef(jobj);return obj_array;
}

请添加图片描述
Native 方法首先调用 JNI 函数 FindClass 获取 Person 类的引用,如果 Person 类加载失败的话, FindClass 会返回 NULL, 然后抛出一个 java.lang.NoClassDefFoundError 异常。
  接下来通过 GetMethodID 获取了类的默认构造函数的ID(下一节会介绍)。并且通过 GetFieldId 获取了Person变量的ID,用于后面的赋值。
  调用 NewObjectArray 创建一个数组,在for循环中 NewObject 实例化 Person类,并通过 SetXXField 函数给实例变量赋值。SetObjectArrayElement 将实例化对象插入数组。
  最后调用 DeleteLocalRef 方法释放局部变量。 DeleteLocalRef 将新创建的 引用从引用表中移除。在 JNI 中,只有 jobject 以及子类属于引用变量,会占用引用表的空间,jint,jfloat,jboolean 等都是基本类型变量,不会占用引用表空间,即不需要释放。引用表最大空间为 512 个,如果超出这个范围,JVM 就会挂掉。

0x02 方法签名

在上面的的例子中,在调用实例变量或者方法,都必须传入一个 jmethodID 的参数。因为在 Java 中存在方法重载(方法名相同,参数列表不同),所以要明确告诉 JVM 调用的是类或实例中的哪一个方法。调用 JNI 的 GetMethodID 函数获取一个 jmethodID 时,需要传入一个方法名称和方法签名,方法名称就是在 Java 中定义的方法名,方法签名的格式为:(形参参数类型列表)返回值。形参参数列表中,引用类型以 L 开头,后面紧跟类的全路径名(需将.全部替换成/),以分号结尾。
  可以通过 javap 命令获取类的签名,以 Person 为例:

javap -s -p app.build.intermediates.classes.all.debug.com.example.gnaix.ndk.Person

参数说明:
-s: 输出内部类型签名
-p: 显示所有类和成员请添加图片描述

感谢大佬的支持,支持原创http://gnaixx.cc/2016/04/07/ndk-array/

相关内容

热门资讯

经典朋友圈早安文案 经典朋友圈早安文案汇总(精选140句)  每一个人只要心里有山巅,即使道路再曲折,也能够到达人生的顶...
调侃男友俏皮句子 调侃男友俏皮句子 (精选85句)  在日常的学习、工作、生活中,大家总免不了要接触或使用句子吧,根据...
打动人心的正能量句子 打动人心的正能量句子  在日常学习、工作或生活中,大家最不陌生的就是句子了吧,根据结构的不同句子可以...
哀悼逝者的句子 哀悼逝者的句子(精选140句)  在平时的学习、工作或生活中,大家一定都接触过一些使用较为普遍的句子...
最新美到极致的惊蛰节气句子 最新美到极致的惊蛰节气句子(精选110句)  在日常学习、工作或生活中,大家都接触过很多优秀的句子吧...
曾经爱情的句子有哪些 关于曾经爱情的句子有哪些  1、曾经,在那个花季的年代,你突然出现在我面前,信诺誓言的对我说,你爱我...
表达兄弟情深的句子 关于表达兄弟情深的句子  在平日的学习、工作和生活里,许多人对一些广为流传的句子都不陌生吧,句子是能...
高情商发圈被秒赞的句子正能量 高情商发圈被秒赞的句子正能量  在平平淡淡的日常中,大家都接触过比较经典的句子吧,句子是能够表达一个...
悼念去世亲人的句子 悼念去世亲人的句子  在日常学习、工作抑或是生活中,大家肯定对各类句子都很熟悉吧,句子能表达一个完整...
七月你好的唯美句子 七月你好的唯美句子  在生活中,我跌倒过。我在嘲笑声中站起来,虽然衣服脏了,但那是暂时的,它可以洗净...
对某人失望心寒的句子 对某人失望心寒的句子(精选120句)  失望,有时候也是一种幸福,因为有所期待所以才会失望。因为有爱...
杨绛我们仨句子赏析 杨绛我们仨句子赏析  《我们仨》是2004年7月生活·读书·新知三联书店出版的图书,作者是杨绛。下面...
唯美心情句子 2022年常用唯美心情句子汇总90句  原来和文字沾上边的孩子从来都是不欢乐的,他们的欢乐象贪玩的小...
常用早安共勉句子微信 常用早安共勉句子微信大合集41句  人生如车,或长途,或短途;人生如戏,或喜,或悲。很多事,过去了,...
感恩父母恩情的句子 关于感恩父母恩情的句子大全  无论在学习、工作或是生活中,大家最不陌生的就是句子了吧,句子是能够表达...
早上发朋友圈正能量的句子   生活没有真正的完美,只有不完美才是最真实的美;生活没有一帆风顺的,只有披荆斩棘才能路路顺;生活没...
深情表白句子 深情表白句子  在日复一日的学习、工作或生活中,大家都接触过很多优秀的句子吧,句子可分为单句和复句,...
描写夏天小雨的好句子 描写夏天小雨的好句子  在平时的学习、工作或生活中,大家都对那些朗朗上口的句子很是熟悉吧,句子由词或...
展望未来的句子 展望未来的句子(精选100句)  在学习、工作或生活中,大家都收藏过令自己印象深刻的句子吧,根据语气...
致敬英雄的句子 致敬英雄的句子  在我们平凡的日常里,大家最不陌生的就是句子了吧,从表达的角度说,句子是最基本的表述...