Jni编程(三)c/c++ 获取java字符串,以及java 获取c/c++创建的对象

 一、Java 层对象作为 opaque references(指针)传递到 JNI 层。 Opaque references 是一种 C/C++ 的指针类型,它指向 JavaVM 内部数据结构。使用这种指针的目的是:不希望 JNI 用户了解 JavaVM 内部数据结构。对 Opaque reference所指结构的操作,都要通过 JNI 方法进行. 比如,"java.lang.String"对象,JNI 层对应的类型为 jstring,对该 opaque reference 的操作要通过 JNIEnv->GetStringUTFChars 进行。

一定要按这种原则编程,千万不要为了效率或容易的取到某个值,绕过 JNI,直接操作 opaque reference.   JNI 是一套完善接口,所有需求都能满足。  在 JNI 中对象的基类即为 jobject. 为方便起见,还定义了 jstring,jclass, jobjectArray 等结构,他们都继承自 jobject。
   使用对应的 JNI 函数把 jstring 转成 C/C++字串。JNI 支持 Unicode/UTF-8 字符编码互转。 Unicode 以 16-bits 值编码;UTF-8 是一种以字节为单位变长格式的字符编码,并与 7-bits ASCII 码兼容。UTF-8 字串与 C 字串一样,以 NULL('\0')做结束符, 当 UTF-8 包含非 ASCII码字符时,以'\0'做结束符的规则不变。7-bit ASCII 字符的取值范围在 1-127 之间,这些字符的值域与 UTF-8 中相同。当最高位被设置时,表示多字节编码。  如下,调用 GetStringUTFChars,把一个 Unicode 字串转成 UTF-8 格式字串,如果你确定字串只包含 7-bit ASCII 字符。这个字串可以使用 C 库中的相关函数,如 printf

 Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) {
        char buf[ 128];  
        const jbyte * str;
        str = ( * env)->GetStringUTFChars(env, prompt, NULL);
        if (str == NULL) {
            return NULL; /* OutOfMemoryError already thrown */
        }
        printf("%s", str);
        ( * env)-> ReleaseStringUTFChars(env, prompt, str);    /* We assume here that the user does not type more than     * 127 characters */
        scanf("%127s", buf);
        return ( * env)->NewStringUTF(env, buf);
    }

 记得检测 GetStringUTFChars 的返回值,因为调用该函数会有内存分配操作,失败后,该函数返回 NULL,并抛 OutOfMemoryError 异常。   如何处理异常,后面会有介绍。JNI 处理异常,不同于 Java 中的 try...catch。在 JNI 中,发生异常,不会改变代码执行轨迹,所以,当返回 NULL,要及时返回,或马上处理异常。
二、释放本地字符串内存

调用 ReleaseStringUTFChars 释放 GetStringUTFChars 中分配的内存(Unicode -> UTF-8转换的原因)。

三、在C/C++中创建Java字符串

使用 JNIEnv->NewStringUTF 构造 java.lang.String;如果此时没有足够的内存,NewStringUTF 将抛 OutOfMemoryError 异常,同时返回 NULL。
四、其它操作JNI字符串的函数

 除了 GetStringUTFChars, ReleaseStringUTFChars, 和 NewStringUTF, JNI 还支持其他
操作 String 的函数供使用。
  GetStringChars 是有 Java 内部 Unicode 到本地 UTF-8 的转换函数,可以调用GetStringLength,获得以 Unicode 编码的字串长度。也可以使用 strlen 计算GetStringUTFChars 的返回值,得到字串长度。  const jchar * GetStringChars(JNIEnv *env, jstring str, jboolean *isCopy); 上述声明中,有 isCopy 参数,当该值为 JNI_TRUE,将返回 str 的一个拷贝;为JNI_FALSE 将直接指向 str 的内容。 注意:当 isCopy 为 JNI_FALSE,不要修改返回值,不然将改变 java.lang.String 的不可变语义。  一般会把 isCopy 设为 NULL,不关心 Java VM 对返回的指针是否直接指向java.lang.String 的内容。  一般不能预知 VM 是否会拷贝 java.lang.String 的内容,程序员应该假设 GetStringChars会为 java.lang.String 分配内存。在 JavaVM 的实现中,垃圾回收机制会移动对象,并为对象重新配置内存。一但 java.lang.String 占用的内存暂时无法被 GC 重新配置,将产生内存
碎片,过多的内存碎片,会更频繁的出现内存不足的假象。  记住在调用 GetStringChars 之后,要调用 ReleaseStringChars 做释放,不管在调用GetStringChars 时为 isCopy 赋值 JNI_TRUE 还是 JNI_FALSE,因不同 JavaVM 实现的原因,
ReleaseStringChars 可能释放内存,也可能释放一个内存占用标记(isCopy 参数的作用,从GetStringChars 返回一个指针,该指针直接指向 String 的内容,为了避免该指针指向的内容被 GC,要对该内存做锁定标记)。
五、:Get/RleaseStringCritical. 简介

 为尽可能的避免内存分配,返回指向 java.lang.String 内容的指针,Java 2 SDK release 1.2 提供了:Get/RleaseStringCritical. 这对函数有严格的使用原则。
  当使用这对函数时,这对函数间的代码应被当做临界区(critical region). 在该代码区,不要调用任何会阻塞当前线程和分配对象的 JNI 函数,如 IO 之类的操作。上述原则,可以避免 JavaVM 执行 GC。因为在执行 Get/ReleaseStringCritical 区的代码
时,GC 被禁用了,如果因某些原因在其他线程中引发了 JavaVM 执行 GC 操作,VM 有死锁的危险:当前线程 A 进入 Get/RelaseStringCritical 区,禁用了 GC,如果其他线程 B 中有 GC请求,因 A 线程禁用了 GC,所以 B 线程被阻塞了;而此时,如果 B 线程被阻塞时已经获得了一个 A 线程执行后续工作时需要的锁;死锁发生了。
 可以嵌套调用 GetStringCritical: 

 jchar *s1,*s2;s1 =(*env)->GetStringCritical(env, jstr1);
 if(s1 ==NULL)
 {   
     ... /* error handling */ 

}

    s2 =(*env)-> GetStringCritical(env, jstr2); 

if(s2 ==NULL)

    {
        ( * env)->ReleaseStringCritical(env, jstr1, s    ... /* error handling */

    } ...     /* use s1 and s2 */ 

 (*env)->  ReleaseStringCritical(env, jstr1, s1);

 (*env)-> ReleaseStringCritical(env, jstr2, s2);

 GetStringCritical 因 VM 实现的原因,会涉及内存操作,所以我们需要检查返回指. 比如,
对于 java.lang.String 来说,VM 内部并不是连续存储的,所以 GetStringCritical 要返回一个连续的字符数组,必然要有内存操作。  为避免死锁,此时应尽量避免调用其他 JNI 方法,只允许调用
GetStringCritical/ReleaseStringCritical,Get/ReleasePrimitiveArrayCritical 因 VM 内部 Unicode 编码的缘故,所以Get/ReleaseStringUTFCritical 这种涉及 Unicode->UTF8 转换要分配内存的函数不支持。

 GetStringRegion/GetStringUTFRegion,向准备好的缓冲区赋值,如下: 


    JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) {      /*assumethepromptstringanduserinputhaslessthan128           characters */
        char outbuf[ 128],inbuf[128];
        int len = ( * env)->GetStringLength(env, prompt);
        ( * env)->GetStringUTFRegion(env, prompt, 0, len, outbuf);
        printf("%s", outbuf);
        scanf("%s", inbuf);
        return ( * env)->NewStringUTF(env, inbuf);
    }
 GetStringUTFRegion 有两个参数,starting index 和 length, 这两个参数以 Unicode 编码计算. 该函数会做边界检查,所以可能抛出 StringIndexOutOfBoundsException。因为该函数不涉及内存操作,所以较 GetStringUTFChars 使用要简单。
 
译注 : 有两个函数:GetStringLength/GetStringUTFLength,前者是 Unicode 编码长度,后者 是 UTF 编码长度。 GetStringUTFRegion 很有用,因为你不能修改 GetStringUTFChars 返回值,所以需要另 外 malloc/strcpy 之后,再操作返回值,耗时费力,不如直接使用 GetStringUTFRegion 来 的简洁、高效。

未完待续..........................

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值