JNI 函数(四)字符串操作
(一)、创建一个字符串
函数原型:jstring NewString(JNIEnv *env, const jchar *unicodeChars, jszie len);
参数:
env:JNI 接口指针
unicodeChars:指向 Unicode 字符串的指针
len:unicode 字符串的长度
返回:
返回一个 Java 字符串对象,如果该字符串无法被创建在,则返回 NULL
异常:
如果内存不足,则抛出 OutOfMemoryError
(二)、获取字符串的长度
函数原型:jsize GetStringLength(JNIEnv *env, jstring string);
返回 Java 字符串的长度( unicode 字符的个数)
参数:
env:JNI 接口指针
string:Java 字符串对象
返回:
返回 Java 字符串的长度
(三)、获取字符串的指针
函数原型:const jchar* GetStringChar(JNIEnv *env, jstring string, jboolean *isCopy);
返回指向字符串的 UNICODE 字符数组的指针,该指针一直有效直到被 ReleaseStringchars() 函数调用。
如果 isCopy 为非空,则在复制完成后将 isCopy 设为 JNI_TRUE。如果没有复制,则设为JNI_FALSE。
参数:
env:JNI接口指针
string:Java字符串对象
isCopy:指向布尔值的指针
返回:
返回一个指向unicode字符串的指针,如果操作失败,则返回NULL
(四)、释放字符串
函数原型:void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
通过 VM,native 代码不会再访问 chars 了。参数 chars 是一个指针。可以通过 GetStringChars() 函数获得。
参数:
env:JNI 接口指针
string:Java 字符串对象
chars:指向 Unicode 字符串的指针
(五)、创建一个UTF-8的字符串
函数原型:jstring NewStringUTF(JNIEnv *env, const char *bytes);
创建一个 UTF-8 的字符串。
参数:
env:JNI 接口指针
bytes:指向 UTF-8 字符串的指针
返回:
Java 字符串对象,如果无法构造该字符串,则为 NULL。
异常:
如果系统内存不足,则抛出 OutOfMemoryError
(六)、获取一个UTF-8的字符串的长度
函数原型:jsize GetStringUTFLength(JNIEnv *env,jstring string);
以字节为单位,返回字符串 UTF-8 的长度。
参数:
env:JNI 接口指针
String:Java 字符串对象
返回:
字符串的 UTF-8 的长度
(七)、获取StringUTFChars的指针
函数原型:const char *GetStringUFTChars(JNIEnv *env, jString string, jboolean *isCopy);
返回指向 UTF-8 字符数组的指针,除非该数组被 ReleaseStringUTFChars() 函数调用释放,否则一直有效。
如果 isCopy 不是NULL,*isCopy 在赋值完成后即被设置为 JNI_TRUE。如果未复制,则设置为 JNI_FALSE。
参数:
env:JNI 接口指针
String:Java 字符串对象
isCopy:指向布尔值的指针
返回:
指向 UTF-8 的字符串指针,如果操作失败,则返回 NULL
(八)、释放UTFChars
函数原型:void ReleaseStringUTFChars(JNIEnv *env,jstring string,const char *urf)
通过虚拟机,native 代码不再访问了 utf 了。utf 是一个指针,可以调用 GetStringUTFChars() 获取。
参数:
env:JNI 接口指针
string:Java 字符串对象
utf:指向 utf-8 字符串的指针
注意:在 JDK/JRE 1.1,程序员可以在用户提供的缓冲区获取基本类型数组元素,从 JDK/JRE1.2 开始,提供了额外方法,这些方法允许在用户提供的缓冲区获取 Unicode 字符( UTF-16 编码)或者是 UTF-8 的字符。这些方法如下:
(九)、1.2新的字符串操作方法
1 截取一个字符串
函数原型:void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf)
在str(Unicode字符)从start位置开始截取len长度放置在buf中。如果越界,则抛出StringIndexOutOfBoundsException。
2 截取一个字符串并将其转换为 UTF-8 格式
函数原型:void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);
将str(Unicode字符串)从start位置开始截取len长度并且将其转换为UTF-8编码,然后将结果防止在buf中。
3 截取一个字符串并将其转换为 UTF-8 格式
函数原型:const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
函数原型:void ReleaseStringCritical(JNIEnv *env, jstring string, cost jchar * carray);
上面这两个函数有点类似于 GetStringChars() 和 ReleaseStringChars() 功能。如果可能的话虚拟机会返回一个指向字符串元素的指针;否则,则返回一个复制的副本。
PS:GetStringChars() 和 ReleaseStringChars() 这里两个函数有很大的限制。在使用这两个函数时,这两个函数中间的代码不能调用任何让线程阻塞或者等待 JVM 的其他线程的本地函数或者 JNI 函数。有了这些限制,JVM 就可以在本地方法持有一个从GetStringCritical 得到的字符串的指指针时,禁止 GC。当 GC 被禁止时,任何线程如果出发 GC 的话,都会被阻塞。而 GetStringChars() 和 ReleaseStringChars() 这两个函数中间的任何本地代码都不可以执行会导致阻塞的调用或者为新对象在 JVM 中分配内存。否则,JVM 有可能死活,想象一下这样的场景:
1、只有当前线程触发的 GC 完成阻塞并释放 GC 时,由其他线程出发的 GC 才可能由阻塞中释放出来继续执行。
2、在这个过程中,当前线程会一直阻塞,因为任何阻塞性调用都需要获取一个正在被其他线程持有的锁,而其他线程正等待 GC。
GetStringChars() 和 ReleaseStringChars() 的交替迭代调用是安全的,这种情况下,它们的使用必须有严格的顺序限制。而且,我们一定要记住检查是否因为内存溢出而导致它的返回值是 NULL。因为 JVM 在执行 GetStringChars() 这个函数时,仍有发生数据复制的可能性,尤其是当 JVM 在内存存储的数组不连续时,为了返回一个指向连续内存空间的指针,JVM 必须复制所有数据。
总之,为了避免死锁,在 GetStringChars() 和 ReleaseStringChars() 之间不要调用任何 JNI 函数。