JNI_Z_09_Java的字符串
ZC: jstring 就是 Java中的String对象
ZC: 10.8 Unicode字符串结尾(http://www.360doc.cn/article/14233282_321497569.html)
从GetStringChars和GetStringCritical两个方法获得的Unicode字符串不是以NULL结尾的,需要调用GetStringLength来获取字符串的长度。一些操作系统,如Windows NT中,Unicode字符串必须以两个'\0'结尾,这样的话,就不能直接把GetStringChars得到的字符串传递给Windows NT系统的API,而必须复制一份并在字符串的结尾加入两个'\0'
ZC: http://www.360doc.cn/article/14233282_321497569.html
“
GetStringChars和ReleaseStringChars获取以Unicode格式编码的字符串。当操作系统支持Unicode编码的字符串时,这些方法很有用。
UTF-8字符串以’\0’结尾,而Unicode字符串不是。如果jstring指向一个Unicode编码的字符串,为了得到这个字符串的长度,可以调用GetStringLength。如果一个jstring指向一个UTF-8编码的字符串,为了得到这个字符串的字节长度,可以调用标准C函数strlen。或者直接对jstring调用JNI函数GetStringUTFLength,而不用管jstring指向的字符串的编码格式。
”
ZC: 个人偏好的统一做法(不管是UTF8还是Unicode) : C++中分配内存 + GetStringRegion/GetStringUTFRegion复制内存 (ZC: ∵结尾非"\0\0"的问题)
字符串操作:
A、生成一个jstring对象
jstring (JNIEnv *)->NewString(const jchar* jstr , int size); // ZC: 第一个参数 : UTF-8编码的字符串指针 和 Unicode(UTF-16)编码的宽字符串指针,都可以?它能自动适应?
ZC: jstring (JNIEnv *)->NewStringUTF(const char* _pchar); // ZC: 这个函数应该 较少用到
B、获取字符串的长度:
jsize (JNIEnv *)->GetStringLength(jstring _jstr);
C、将jstring对象 拷贝到 const jchar*指针字符串
C.1、拷贝Java字符串并以UTF-8编码传入jstr
(JNIEnv *)->GetStringRegion(jstring _jstr, jsize start, jsize len, jchar* _pjchar);
C.2、拷贝Java字符串并以UTF-16编码传入jstr
(JNIEnv *)->GetStringUTFRegion(jstring _jstr, jsize start, jsize len, char* _pchar);
参数 : _jstr是一个jstring对象,start是拷贝字符串的开始位置,len是拷贝字符串的长度,jstr是目标指针字符串
(1)、在Java1.2出来的函数,这个函数的动作是把Java字符串的内容直接拷贝到C/C++的字符串数组中,在呼叫这个函数之前必须有一个C/C++分配出来的字符串(具体看下面的例子),然后传入到这个函数中进行字符串的拷贝
(2)、由于C/C++中分配内存开销相对小,而且Java中的String内容拷贝的开销可以忽略,更好的一点是此函数不分配内存,不会抛出OutOfMemoryError异常
(3)、ZC: 上面出现的"这个函数"、"此函数"应该值得就是 GetStringRegion 和 GetStringUTFRegion
D、将jstring对象转化成const jchar*字符串指针
D.1、const jchar* (JNIEnv *)->GetStringChars(jstring _jstr, jboolean* copied);
返回一个UTF-16编码的宽字符串(jchar*)
参数:
_jstr 是字符串对象
copied是指传入的是一个jboolean指针,用来标识是否对Java的String对象进行了拷贝的,如果传入的这个jboolean指针不是NULL,则它会给该指针所指向的内存传入JNI_TRUE或JNI_FALSE标识是否进行了拷贝,传入NULL表示不关心是否拷贝字符串,它就不会给jboolean* 指向的内存赋值
其对应的释放内存指针的方法:
(JNIEnv *)->ReleaseStringChars(jstring _jstr, const jchar* _pjchar);
参数:
_jstr 是jstring对象,
_pjchar是字符串指针
D.2、const char* (JNIEnv *)->GetStringUTFChars(jstring _jstr , jboolean* copied)
这个方法是可以取得UTF-8编码的字符串(char*)
参数的含义和GetStringChars方法是一样的
这个方法也有对应的一个释放内存的方法:
(JNIEnv *)->ReleaseStringUTFChars(jstring _jstr , const char* _pchar);
参数的含义和上面的ReleaseStringChars方法的参数的含义是一样的
需要注意的是:
“(来自 http://www.360doc.cn/article/14233282_321497569.html)
当从JNI函数GetStringChars中返回得到字符串B时,如果B是原始字符串java.lang.String的拷贝,则isCopy被赋值为JNI_TRUE。如果B和原始字符串指向的是JVM中的同一份数据,则isCopy被赋值为JNI_FALSE。当isCopy值为JNI_FALSE时,本地代码决不能修改字符串的内容,否则JVM中的原始字符串也会被修改,这会打破JAVA语言中字符串不可变的规则。
通常,因为你不必关心JVM是否会返回原始字符串的拷贝,你只需要为isCopy传递NULL作为参数。
JVM是否会通过拷贝原始Unicode字符串来生成UTF-8字符串是不可以预测的,程序员最好假设它会进行拷贝,而这个操作是花费时间和内存的。一个典型的JVM会在heap上为对象分配内存。一旦一个JAVA字符串对象的指针被传递给本地代码,GC就不会再碰这个字符串。换言之,这种情况下,JVM必须pin这个对象。可是,大量地pin一个对象是会产生内存碎片的,因为,虚拟机会随意性地来选择是复制还是直接传递指针。
当你不再使用一个从GetStringChars得到的字符串时,不管JVM内部是采用复制还是直接传递指针的方式,都不要忘记调用ReleaseStringChars。根据方法GetStringChars是复制还是直接返回指针,ReleaseStringChars会释放复制对象时所占的内存,或者unpin这个对象。
” ZC: 这里的 JVM的pin操作,没有查到具体的解释...
E、将jstring对象转化成const jchar*字符串指针 (ZC: 带 临界区 操作[互斥/死锁])
const jchar* (JNIEnv *)->GetStringCritical(jstring _jstr, jboolean* copied);
参数:
_jstr 是字符串对象,
copied 上面的方法已经做了解释了,这里就不多说了
这个方法的作用是为了增加直接传回指向Java字符串的指针的可能性(而不是拷贝),JDK1.2出来了新的函数GetStringCritical/ReleaseStringCritical,在GetStringCritical/ReleaseStringCritical之间是一个关键区,在这个关键区域之间不能呼叫JNI的其他函数和会造成当前线程中断,或是会让当前线程等待的任何本地代码,否则将造成关键区代码执行期间垃圾回收器停止运作,任何触发垃圾回收器的线程也会暂停,其他的触发垃圾回收器的线程不能前进直到当前线程结束而激活垃圾回收器
在关键区域中千万不要出现中断操作,或是在JVM中分配任何新对象,否则会造成JVM死锁
虽说这个函数会增加直接传回指向Java字符串的指针的可能性,不过还是会根据情况传回拷贝过的字符串 (ZC: 好蛋疼...)
不支持GetStringUTFCritical,没有这样的函数,由于Java字符串用的是UTF-16,要转成UTF-8编码的字符串始终需要进行一次拷贝,所以没有这样的函数
这个方法和第四个方法(ZC: GetStringChars)是一样的功能
其对应的释放内存指针的方法:
(JNIEnv *)->ReleaseStringCritical(jstring _jstr, const jchar* _pjchar);
1、
2、
3、
4、
5、