今天我们继续学习JNI数组,此篇文章仅作为笔记,以防以后忘记
JNI把java类型分为俩类,基本数据类型和引用数据类型,引用数据类型统一用jobject来表示,数组也一样,也分为基本数据类型和引用数据类型,引用数据类型为jobjectarray来表示
我们先来分析一下基本数据类型的数组相关的API
返回一个基本数据类型的数组,其中PrimitiveType
指的是基本数据类型,比如你要获取int的数据类型的数组,那么PrimitiveType
就是int
NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env,
ArrayType array, jboolean *isCopy);
*isCopy
为JNI_TRUE时,指针指向原数组的拷贝,*isCop
为JNI_FALSE时,指针指向原数组如果函数指针指向原数组,那么所有的修改都是在原数组上进行的,如果函数指针指向拷贝数组,那么所有的修改都是在拷贝数组上进行的,元素组不受影响
我么你需要保证当拷贝发生了,修改的数据可以同步到原数组,Release
就是这个作用
void Release<PrimitiveType>ArrayElements(JNIEnv *env,
ArrayType array, NativeType *elems, jint mode);
0
表示把修改同步到原数组,并释放本地数组JNI_COMMIT
:把修改同步到原数组,但是不释放本地数组JNI_ABORT
:不把修改同步到原数组,但是释放本地数组当Get< PrimitiveType >ArrayElements函数不发生拷贝,那么mode不起任何作用
java代码
public class TextJniArray {
static {
System.loadLibrary("textjniarray");
}
public native void textArray(int[] array);
}
C代码
#include
#include
static void native_text_array(JNIEnv *env, jobject jobject1, jintArray array) {
//获取数组长度
jsize length = env->GetArrayLength(array);
//获取本地数组
jint *native_intaray = env->GetIntArrayElements(array, NULL);
//操作本地数组
for(int i=0;i<length;i++){
native_intaray[i]+=100;
}
//释放本地数组
env->ReleaseIntArrayElements(array,native_intaray,0);
}
static const JNINativeMethod nativeMethod[] = {
{
"textArray", "([I)V", (void *) native_text_array}
};
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.TextJniArray");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
return result;
}
}
调用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int[] a = {
0, 1, 2, 3, 4, 5};
for (int i = 0; i < 6; i++) {
Log.d("mmm调用前数组数据", a[i] + "/");
}
new TextJniArray().textArray(a);
for (int i = 0; i < 6; i++) {
Log.d("mmm调用后数组数据", a[i] + "/");
}
}
打印
D/mmm调用前数组数据: 0/
D/mmm调用前数组数据: 1/
D/mmm调用前数组数据: 2/
D/mmm调用前数组数据: 3/
D/mmm调用前数组数据: 4/
D/mmm调用前数组数据: 5/
D/mmm调用后数组数据: 100/
D/mmm调用后数组数据: 101/
D/mmm调用后数组数据: 102/
D/mmm调用后数组数据: 103/
D/mmm调用后数组数据: 104/
D/mmm调用后数组数据: 105/
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,
jsize start, jsize len, NativeType *buf);
这个函数的作用是拷贝java数组ArrayType array
到本地数组NativeType *buf
,如果在本地数组上修改,需要同步到java数组,那么需要调用Set
void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,
jsize start, jsize len, const NativeType *buf);
这个函数的作用是把本地数组NativeType *buf
数据写回到java原数组中,起始点为start,长度为len
这次只贴C代码
static void native_text_array1(JNIEnv *env, jobject jobject1, jintArray array) {
//获取数组长度
jsize length = env->GetArrayLength(array);
//创建本地缓存数组
jint native_array[length - 2];
//进行数组考别
env->GetIntArrayRegion(array, 2, length - 2, native_array);
for (int i = 0; i < length - 2; ++i) {
native_array[i] += 100;
}
//把修改数据写回原数组
env->SetIntArrayRegion(array, 2, length - 2, native_array);
}
打印
D/mmm调用前数组数据: 0/
D/mmm调用前数组数据: 1/
D/mmm调用前数组数据: 2/
D/mmm调用前数组数据: 3/
D/mmm调用前数组数据: 4/
D/mmm调用前数组数据: 5/
D/mmm调用后数组数据: 0/
D/mmm调用后数组数据: 1/
D/mmm调用后数组数据: 102/
D/mmm调用后数组数据: 103/
D/mmm调用后数组数据: 104/
D/mmm调用后数组数据: 105/
这个方法可以在JNI层创建数组,然后返回给java
C代码
static jintArray native_text_array2(JNIEnv *env, jobject jobject1) {
jintArray array = env->NewIntArray(2);
jint *native_array = env->GetIntArrayElements(array, NULL);
for (int i = 0; i < 2; ++i) {
native_array[i] = 100 + i;
}
env->ReleaseIntArrayElements(array, native_array, 0);
return array;
}
调用
int[] a = new TextJniArray().textArray2();
for (int i = 0; i < 2; i++) {
Log.d("mmm数组数据", a[i] + "/");
}
打印
D/mmm数组数据: 100/
D/mmm数组数据: 101/
先看下需要用的API
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
获取数组array
在索引index
下的元素
jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);
用于判断对象obj,是不是class类的实例对象
准备引用数据类型
public class Person {
public String name;
public Person(String name) {
this.name = name;
}
public void say() {
Log.d("mmm", name + "在说话");
}
}
准备native方法
public native void textArray3(Person[] persons);
准备C实现
static void native_text_array3(JNIEnv *env, jobject jobject1, jobjectArray jobjectArray1) {
//获取数组长度
jsize length = env->GetArrayLength(jobjectArray1);
//获取person的class对象
jclass jclass_person = env->FindClass("com.taobao.alinnkit.ndk1.Person");
if (jclass_person == NULL) {
return;
}
//获取方法
jmethodID jmethodId_say = env->GetMethodID(jclass_person, "say", "()V");
if (jmethodId_say == NULL) {
return;
}
for (int i = 0; i < length; ++i) {
//获取java引用数组中的元素
jobject jobject2 = env->GetObjectArrayElement(jobjectArray1, i);
//判断元素的类型
if (env->IsInstanceOf(jobject2, jclass_person)) {
//调用元素的方法
env->CallVoidMethod(jobject2, jmethodId_say);
}
}
}
调用
Person[] people = new Person[2];
people[0] = new Person("jj");
people[1] = new Person("oj");
new TextJniArray().textArray3(people);
打印数据
D/mmm: jj在说话
D/mmm: oj在说话
jobjectArray NewObjectArray(JNIEnv *env, jsize length,
jclass elementClass, jobject initialElement);
作用是在JNI层创建引用微数据类型数组,NewObjectArray
函数会根据elementClass
类型创建一个长度length
的引用数据类型数组
如果指定第四个参数则会初始化数组,如果指定为NULL,则数组的所有元素为null
void SetObjectArrayElement(JNIEnv *env, jobjectArray array,
jsize index, jobject value);
为数组array,设置索引index下的值为value
jobject NewObject(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);
jobject NewObjectA(JNIEnv *env, jclass clazz,
jmethodID methodID, const jvalue *args);
jobject NewObjectV(JNIEnv *env, jclass clazz,
jmethodID methodID, va_list args);
这三个参数区别就是传入的参数不一样,但是作用一样
FindClass
来寻找GetMethodID
来获取,传入的参数名为
,参数名的返回值为Vjobject AllocObject(JNIEnv *env, jclass clazz);
这个函数会给clazz类的对象分配内存,而不用调用class类的构造函数,并返回一个执向这个对象的引用
准备java的native方法
public native Person[] textArray4(int length);
准备C实现
static jobjectArray native_text_array4(JNIEnv *env, jobject jobject1, jint length) {
jclass jclass_person = env->FindClass("com.taobao.alinnkit.ndk1.Person");
if (jclass_person == NULL) {
return NULL;
}
//获取person的构造方法
jmethodID jmethodId_gouzao = env->GetMethodID(jclass_person, "" , "(Ljava/lang/String;)V");
if (jmethodId_gouzao == NULL) {
return NULL;
}
//创建引用数组
jobjectArray array_person = env->NewObjectArray(length, jclass_person, NULL);
for (int i = 0; i < length; ++i) {
jobject person = NULL;
if (i == 0) {
//构造方法创建对象
person = env->NewObject(jclass_person, jmethodId_gouzao, env->NewStringUTF("小红"));
}
if (i == 1) {
person = env->AllocObject(jclass_person);
//直接分配内存
jmethodID setname = env->GetMethodID(jclass_person, "setName", "(Ljava/lang/String;)V");
env->CallVoidMethod(person, setname, env->NewStringUTF("小明"));
}
//把初始化好的对象赋值给数组
env->SetObjectArrayElement(array_person, i, person);
//释放局部变量
env->DeleteLocalRef(person);
}
return array_person;
}
调用
Person[] people1 = new TextJniArray().textArray4(2);
for (int i = 0; i < people1.length; i++) {
people1[i].say();
}
打印数据
D/mmm: 小红在说话
D/mmm: 小明在说话
参考
https://juejin.im/post/5d2ed0ac6fb9a07f0655a267