JNI 操作 java 数组

在 jni 函数中对 java 数组的操作主要包含以下几类:

  1. GetArrayLength(jarray array)

    用于返回 java 数组的数据长度

    jstring stringFromJNI(JNIEnv *env, jobject thiz, jintArray intArray){
        __android_log_print(ANDROID_LOG_INFO, "native", "stringFromJNI");
        
        jsize size = env->GetArrayLength(intArray);
        __android_log_print(ANDROID_LOG_INFO, "native", "data size %d", size);
    
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
  2. Get<Type>ArrayElements(<Type>Array arr , jboolean* isCopide) 与Release<Type>ArrayElements(<Type>Array arr , <Type>* array , jint mode)

    jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy)
    { return functions->GetBooleanArrayElements(this, array, isCopy); }
    jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy)
    { return functions->GetByteArrayElements(this, array, isCopy); }
    jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy)
    { return functions->GetCharArrayElements(this, array, isCopy); }
    jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy)
    { return functions->GetShortArrayElements(this, array, isCopy); }
    jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
    { return functions->GetIntArrayElements(this, array, isCopy); }
    jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy)
    { return functions->GetLongArrayElements(this, array, isCopy); }
    jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy)
    { return functions->GetFloatArrayElements(this, array, isCopy); }
    jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy)
    { return functions->GetDoubleArrayElements(this, array, isCopy); }
    
    void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems, jint mode)
    { functions->ReleaseBooleanArrayElements(this, array, elems, mode); }
    void ReleaseByteArrayElements(jbyteArray array, jbyte* elems, jint mode)
    { functions->ReleaseByteArrayElements(this, array, elems, mode); }
    void ReleaseCharArrayElements(jcharArray array, jchar* elems, jint mode)
    { functions->ReleaseCharArrayElements(this, array, elems, mode); }
    void ReleaseShortArrayElements(jshortArray array, jshort* elems, jint mode)
    { functions->ReleaseShortArrayElements(this, array, elems, mode); }
    void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode)
    { functions->ReleaseIntArrayElements(this, array, elems, mode); }
    void ReleaseLongArrayElements(jlongArray array, jlong* elems, jint mode)
    { functions->ReleaseLongArrayElements(this, array, elems, mode); }
    void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems, jint mode)
    { functions->ReleaseFloatArrayElements(this, array, elems, mode); }
    void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems, jint mode)
    { functions->ReleaseDoubleArrayElements(this, array, elems, mode); }
    

    其中 Get<Type>ArrayElements 函数可以把Java基本类型的数组转换到C/C++中的数组,有两种处理方式,一种是拷贝一份传回本地代码,另一个是把指向Java数组的指针直接传回到本地代码中;该方法有两个参数,第一个参数指向了需要处理的数组,第二个参数用于表示把Java基本类型的数组转换到C/C++中的数组具体使用的处理方式,也就是说是否发生了 数组copy;

    在完成数组数据处理后,调用 Release<Type>ArrayElements 方法,这个函数可以选择将如何处理 Java 与 C++ 的数组,是提交,还是撤销等,内存释放还是不释放等;该方法有三个参数,第一个参数指向了 java 数组,第二个参数指向调用 Get<Type>ArrayElements 函数返回的数组指针,第三个参数用于决定数组是提交,还是撤销等,内存释放还是不释放,其中包含三种模式:

    1. mode = 0
      • 原始数据: 对象数组将不会被限制.
      • 拷贝数据: 数据将会拷贝回原始数据, 同时释放拷贝数据.
    2. mode = JNI_COMMIT (1)
      • 原始数据: 什么都不作.
      • 拷贝数据: 数据将会拷贝回原始数据, 不释放拷贝数据.
    3. mode = JNI_ABORT (2)
      • 原始数据: 对象数组将不会被限制, 之前的数据操作有效
      • 拷贝数据: 释放拷贝数据, 之前的任何数据操作会丢弃.
    jstring stringFromJNI(JNIEnv *env, jobject thiz, jintArray intArray){
        __android_log_print(ANDROID_LOG_INFO, "native", "stringFromJNI");
        
        jboolean isCp = JNI_FALSE;
        jint* coldata = env->GetIntArrayElements(intArray, &isCp);
        __android_log_print(ANDROID_LOG_INFO, "native", "isCopy %d", isCp);
        jsize size = env->GetArrayLength(intArray);
        for (int j = 0; j < size; j++) {
            coldata[j] += 1;
        }
        env->ReleaseIntArrayElements(intArray, coldata, 0);
    
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    

    再具体使用过程中,先调用 GetIntArrayElements 获取 java 数组的指针以及是否发生了拷贝;然后就可以对数组数据进行处理,在上诉代码中只是做了 +1 操作,然后调用 ReleaseIntArrayElements 方法,其中 mode 为 0,也就是说将数据处理结构同步到 java 数组,并释放数组指针;

  3. GetPrimitiveArrayCritical(jarray arr , jboolean* isCopied) 与 ReleasePrimitiveArrayCritical(jarray arr , void* array , jint mode)

    这两个方法与上面的 Get<Type>ArrayElements 与Release<Type>ArrayElements 方法非常像,但是有一点很重要的不同:GetPrimitiveArrayCritical 更倾向于也更容易获取 java 中数组的指针而不进行拷贝,这也导致在调用 GetPrimitiveArrayCritical 与 ReleasePrimitiveArrayCritical 不能执行耗时很长的操作或者需要阻塞并等待的操作,而且在这之间 JVM 很可能会暂时禁用GC;

    jstring stringFromJNI(JNIEnv *env, jobject thiz, jintArray intArray){
    
        __android_log_print(ANDROID_LOG_INFO, "native", "stringFromJNI");
        jboolean isCp = JNI_FALSE;
        jint* coldata = static_cast<jint *>(env->GetPrimitiveArrayCritical(intArray, &isCp));
        __android_log_print(ANDROID_LOG_INFO, "native", "isCopy %d", isCp);
        jsize size = env->GetArrayLength(intArray);
        for (int j = 0; j < size; j++) {
            coldata[j] += 1;
        }
        env->ReleasePrimitiveArrayCritical(intArray, coldata, 0);
    
     std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
  4. Get<Type>ArrayRegion(<Type>Array arr , jsize start , jsize len , <Type>* buffer)

    void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf)
    { functions->GetBooleanArrayRegion(this, array, start, len, buf); }
    void GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf)
    { functions->GetByteArrayRegion(this, array, start, len, buf); }
    void GetCharArrayRegion(jcharArray array, jsize start, jsize len,jchar* buf)
    { functions->GetCharArrayRegion(this, array, start, len, buf); }
    void GetShortArrayRegion(jshortArray array, jsize start, jsize len,jshort* buf)
    { functions->GetShortArrayRegion(this, array, start, len, buf); }
    void GetIntArrayRegion(jintArray array, jsize start, jsize len,jint* buf)
    { functions->GetIntArrayRegion(this, array, start, len, buf); }
    void GetLongArrayRegion(jlongArray array, jsize start, jsize len,jlong* buf)
    { functions->GetLongArrayRegion(this, array, start, len, buf); }
    void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len,jfloat* buf)
    { functions->GetFloatArrayRegion(this, array, start, len, buf); }
    void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,jdouble* buf)
    { functions->GetDoubleArrayRegion(this, array, start, len, buf); }
    

    在 jni 预先开辟一段内存,然后把 Java 数组拷贝到这段内存中; 该方法有四个参数,第一个指向的是 java 数组,第二个参数表示开始位置,第三个参数表示拷贝的长度,第四个指向目标缓冲区;

    jstring stringFromJNI(JNIEnv *env, jobject thiz, jintArray intArray){
        __android_log_print(ANDROID_LOG_INFO, "native", "stringFromJNI");
        
        jsize size = env->GetArrayLength(intArray);
        jint* data = new jint[size];
        env->GetIntArrayRegion(intArray, 0, size, data);
        for (int j = 0; j < size; j++) {
            __android_log_print(ANDROID_LOG_INFO, "native", "data %d", data[j]);
        }
        
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
  5. Set<Type>ArrayRegion(<Type>Array arr , jsize start , jsize len , const <Type>* buffer)

    void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,const jboolean* buf)
    { functions->SetBooleanArrayRegion(this, array, start, len, buf); }
    void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,const jbyte* buf)
    { functions->SetByteArrayRegion(this, array, start, len, buf); }
    void SetCharArrayRegion(jcharArray array, jsize start, jsize len,const jchar* buf)
    { functions->SetCharArrayRegion(this, array, start, len, buf); }
    void SetShortArrayRegion(jshortArray array, jsize start, jsize len,const jshort* buf)
    { functions->SetShortArrayRegion(this, array, start, len, buf); }
    void SetIntArrayRegion(jintArray array, jsize start, jsize len,const jint* buf)
    { functions->SetIntArrayRegion(this, array, start, len, buf); }
    void SetLongArrayRegion(jlongArray array, jsize start, jsize len,const jlong* buf)
    { functions->SetLongArrayRegion(this, array, start, len, buf); }
    void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,const jfloat* buf)
    { functions->SetFloatArrayRegion(this, array, start, len, buf); }
    void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,const jdouble* buf)
    { functions->SetDoubleArrayRegion(this, array, start, len, buf); }
    

    把 Java 数组中的指定范围的元素用 jni 数组中的元素来赋值,该方法有四个参数,第一个指向的是目标 java 数组,第二个参数表示开始位置,第三个参数表示拷贝的长度,第四个指向 jni 数组;

    jstring stringFromJNI(JNIEnv *env, jobject thiz, jintArray intArray){
        __android_log_print(ANDROID_LOG_INFO, "native", "stringFromJNI");
        
        jsize size = env->GetArrayLength(intArray);
        jint* data = new jint[size];
        for (int j = 0; j < size; j++) {
            data[j] = j;
        }
        env->SetIntArrayRegion(intArray, 0, size, data);
    
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
  6. <Type>Array New<Type>Array(jsize sz)

    jbooleanArray NewBooleanArray(jsize length)
    { return functions->NewBooleanArray(this, length); }
    jbyteArray NewByteArray(jsize length)
    { return functions->NewByteArray(this, length); }
    jcharArray NewCharArray(jsize length)
    { return functions->NewCharArray(this, length); }
    jshortArray NewShortArray(jsize length)
    { return functions->NewShortArray(this, length); }
    jintArray NewIntArray(jsize length)
    { return functions->NewIntArray(this, length); }
    jlongArray NewLongArray(jsize length)
    { return functions->NewLongArray(this, length); }
    jfloatArray NewFloatArray(jsize length)
    { return functions->NewFloatArray(this, length); }
    jdoubleArray NewDoubleArray(jsize length)
    { return functions->NewDoubleArray(this, length); }
    

    使用 NewBooleanArray(jsize length) 可以返回一个指定长度的 java 数组;

  7. jobject GetObjectArrayElement(jobjectArray array, jsize index) 与 void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)

    上面所述的对于数组的操作除了 GetArrayLength 方法是对于基本类型数组与对象数组通用的,其它均是用于操作基本类型数组;GetObjectArrayElement 与 SetObjectArrayElement 提供了对于对象数组的操作 GetObjectArrayElement 方法用于返回对象数组中指定位置的对象;SetObjectArrayElement 用于更新对象数组中指定位置的对象;

  8. jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement)

    之前提到 New<Type>Array 方法用于创建 java 基本类型数组,对应的 NewObjectArray 用于创建 java 对象数组,该方法包含三个参数,第一个参数表示数组长度,第二个参数表示类,第三个参数表示元素初始值;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容