Android JNI和NDK學習(五):JNI調用數組

概述

今天咱們繼續學習JNI數組,此篇文章僅做爲筆記,以防之後忘記java

數組

JNI把java類型分爲倆類,基本數據類型和引用數據類型,引用數據類型統一用jobject來表示,數組也同樣,也分爲基本數據類型和引用數據類型,引用數據類型爲jobjectarray來表示android

基本數據類型的數組

咱們先來分析一下基本數據類型的數組相關的API數組

Get< PrimitiveType >ArrayElements

返回一個基本數據類型的數組,其中PrimitiveType指的是基本數據類型,好比你要獲取int的數據類型的數組,那麼PrimitiveType就是int緩存

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

複製代碼
  • ArrayType array:表示java的數組對象
  • jboolean *isCopy:*isCopy 爲JNI_TRUE時,指針指向原數組的拷貝,*isCop爲JNI_FALSE時,指針指向原數組

若是函數指針指向原數組,那麼全部的修改都是在原數組上進行的,若是函數指針指向拷貝數組,那麼全部的修改都是在拷貝數組上進行的,元素組不受影響ide

我麼你須要保證當拷貝發生了,修改的數據能夠同步到原數組,Release<PrimitiveType>ArrayElements就是這個做用函數

Release< PrimitiveType >ArrayElements

void Release<PrimitiveType>ArrayElements(JNIEnv *env,
                ArrayType array, NativeType *elems, jint mode);
複製代碼
  • ArrayType array:原數組java對象
  • NativeType *elems:JNI數組類型的指針,本地數組
  • jint mode:0表示把修改同步到原數組,並釋放本地數組 JNI_COMMIT:把修改同步到原數組,可是不釋放本地數組 JNI_ABORT:不把修改同步到原數組,可是釋放本地數組

當Get< PrimitiveType >ArrayElements函數不發生拷貝,那麼mode不起任何做用post

實戰

java代碼學習

public class TextJniArray {

    static {
        System.loadLibrary("textjniarray");
    }


    public native void textArray(int[] array);


}
複製代碼

C代碼ui

#include <jni.h>
#include <android/log.h>

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;
    }
}

複製代碼

調用this

@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/
複製代碼

Get< PrimitiveType >ArrayRegion

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

複製代碼
  • ArrayType array:java數組對象
  • jsize start:拷貝開始位置
  • jsize len:拷貝的長度
  • NativeType *buf:本地緩存數組

這個函數的做用是拷貝java數組ArrayType array到本地數組NativeType *buf,若是在本地數組上修改,須要同步到java數組,那麼須要調用Set<PrimitiveType>ArrayRegion

Set< PrimitiveType >ArrayRegion

void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,
            jsize start, jsize len, const NativeType *buf);
複製代碼
  • ArrayType array:java原始數組
  • 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/
複製代碼

New< PrimitiveType >Array

這個方法能夠在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

GetObjectArrayElement

jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
複製代碼

獲取數組array在索引index下的元素

IsInstanceOf

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在說話
複製代碼

NewObjectArray

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

複製代碼

做用是在JNI層建立引用微數據類型數組,NewObjectArray函數會根據elementClass類型建立一個長度length的引用數據類型數組

若是指定第四個參數則會初始化數組,若是指定爲NULL,則數組的全部元素爲null

SetObjectArrayElement

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

複製代碼

爲數組array,設置索引index下的值爲value

NewObject & NewObjectA & NewObjectV

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);

複製代碼

這三個參數區別就是傳入的參數不同,可是做用同樣

  • 參數class表明,java的class對象,能夠經過FindClass來尋找
  • 參數methodID指的是java的構造函數,經過GetMethodID來獲取,傳入的參數名爲<init>,參數名的返回值爲V

AllocObject

jobject 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, "<init>", "(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: 小明在說話
複製代碼

參考

juejin.im/post/5d2ed0…

相關文章
相關標籤/搜索