【混合编程jni 】第六篇之native 中字符串和数组的操作

香菜聊游戏 发表于 2022/06/26 21:29:36 2022/06/26
【摘要】 继续JNI的知识点,今天看下字符串和数组的一些操作这两个是比较特殊的存在字符串操作编码格式Java默认使用Unicode编码,C/C++默认使用UTF编码在本地代码中操作字符串的时候,JNI支持字符串在Unicode和UTF-8两种编码之间转换。GetStringUTFChars能够把一个jstring指针(指向JVM内部的Unicode字符序列)转换成一个UTF-8格式的C字符串。访问字符...

继续JNI的知识点,今天看下字符串和数组的一些操作

这两个是比较特殊的存在

字符串操作

编码格式

Java默认使用Unicode编码,C/C++默认使用UTF编码

在本地代码中操作字符串的时候,JNI支持字符串在Unicode和UTF-8两种编码之间转换。

GetStringUTFChars能够把一个jstring指针(指向JVM内部的Unicode字符序列)转换成一个UTF-8格式的C字符串。

访问字符串

一般来说,在从JVM内部获取一个字符串之后。

JVM内部会分配一块新的内存,拷贝原来的字符串,以便本地代码访问和改动。

即然有内存分配。在使用完之后就应该释放。

ReleaseStringUTFChars()和GetStringUTFChars()必须成对出现,用来释放内存和引用,使对象可以被GC回收。

创建字符串

调用NewStringUTF函数会构建一个新的java.lang.String字符串对象。

JVM不能分配足够的内存,NewStringUTF会抛出一个OutOfMemoryError异常,并返回NULL

下面是一套连招

jstring NewStringUTF(JNIEnv *env, const char *bytes); 
jsize GetStringUTFLength(JNIEnv *env, jstring string);
void ReleaseStringUTFChars(JNIEnv *env, jstring string,const char *utf);

两组函数 GetStringRegion 和 GetStringUTFRegion

void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);

上述两个函数是复制一段内容倒buff 数组中,避免了全部拷贝。

推荐使用GetStringUTFRegion

GetStringCritical和ReleaseStringCritical

const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);
GetStringCritical得到的是一个指向JVM内部字符串的直接指针,获取这个直接指针后会导致暂停GC线程

这里传递的是jvm内部的指针,一定要小心,慎用

数组操作

数组的操作命名规则都是 operate + PrimitiveType + Array

先通过GetXXXArrayElements函数把简单类型的数组转化成本地类型的数组,

并返回其数组的指针,然后通过该指针来对拷贝数组进行处理。

对拷贝数组处理完后,通过ReleaseXXXArrayElements函数把修改后的拷贝数组的反射到java数组,

基本操作

//    获得数组长度
jsize (GetArrayLength)(JNIEnv env, jarray array);
//    创建数组
jobjectArray NewObjectArray (JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
//    获取元素
NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env,ArrayType array, jboolean *isCopy);
//    设置数据
void SetObjectArrayElement(JNIEnv *env, jobjectArray array,jsize index, jobject value)
//    释放元素
void Release<PrimitiveType>ArrayElements(JNIEnv *env,ArrayType array, NativeType *elems, jint mode);

elems 参数是一个通过使用对应的GetPrimitiveTypeArrayElements() 函数由 array 导出的指针。

必要时,该函数将把对 elems 的修改复制回基本类型数组。mode参数将提供有关如何释放数组缓冲区的信息。

如果elems 不是 array 中数组元素的副本,mode将无效。

否则,mode 将具有下表所述的功能:

image.png

局部访问

//    将数组内容复制到 C 缓冲区内
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,jsize start, jsize len, NativeType *buf);
//    将缓冲区内的内容复制到数组上
void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,jsize start, jsize len, const NativeType *buf);

用来将基本类型数组某一区域复制到缓冲区中的一组函数/将基本类型数组的某一区域从缓冲区中复制回来的一组函数

直接访问

void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);

返回一个指定基础数据类型数组的直接指针,在这两个操作之间不能做任何阻塞的操作

来个例子

传递数组到C++

  // Java 传递 数组 到 C++ 进行数组求和
    private native int intArraySum(int[] intArray, int size);

C++ 侧代码

JNIEXPORT jint JNICALLJava_com_demo_intArraySum(JNIEnv *env, jobject instance,
                                                             jintArray intArray_, jint num) 
{
    jint *intArray;
    int sum = 0;
    // 操作方法一:
    // 如同 getUTFString 一样,会申请 native 内存
    intArray = env->GetIntArrayElements(intArray_, NULL);
    if (intArray == NULL) {
        return 0;
    }
    // 得到数组的长度
    int length = env->GetArrayLength(intArray_);
    cout<<"array length is  "<<length;
    for (int i = 0; i < length; ++i) {
        sum += intArray[i];
    }
    cout<<"sum is  " <<sum);
 
    // 操作方法二:
    jint buf[num];
    // 通过 GetIntArrayRegion 方法来获取数组内容
    env->GetIntArrayRegion(intArray_, 0, num, buf);
    sum = 0;
    for (int i = 0; i < num; ++i) {
        sum += buf[i];
    }
    cout<<"sum is  "<<sum;
    // 使用完了别忘了释放内存
    env->ReleaseIntArrayElements(intArray_, intArray, 0);
    return sum;
}

C++返回数组给Java

// 从 C++ 返回基本数据类型数组 
private native int[] returnIntArray(int num);
/**
 * 从 Native 返回 int 数组,主要调用 set<Type>ArrayRegion 来填充数据,其他数据类型类似操作
 */
extern "C"
JNIEXPORT jintArray JNICALLJava_demo_returnIntArray(JNIEnv *env, jobject instance,
                                                             jint num) 
{
    jintArray intArray;
    intArray = env->NewIntArray(num);
 
    jint buf[num];
    for (int i = 0; i < num; ++i) {
        buf[i] = i * 2;
    }
 
    // 使用 setIntArrayRegion 来赋值
    env->SetIntArrayRegion(intArray, 0, num, buf);
    return intArray;
}

总结

这篇主要写了字符串和数组的操作

基本上要记住的就是get ,set ,region 以及release 几个关键词

申请内存,释放内存,获取元素,操作元素



【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。