【混合编程jni 】第六篇之native 中字符串和数组的操作
继续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 将具有下表所述的功能:
局部访问
// 将数组内容复制到 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 几个关键词
申请内存,释放内存,获取元素,操作元素
- 点赞
- 收藏
- 关注作者
评论(0)