[JNI]开发之旅(9)JNI函数字符串处理

通过前面章节接触到字符串类型,知道字符串类型的数据是不能直接操作的。本节来介绍不能操作的原因及操作字符串的一些函数。

JNI 把 Java 中的所有对象当作一个C指针传递到本地方法中,这个指针指向 JVM 中的内部数据结构,而内部的数据结构在内存中的存储方式是不可见的。只能从 JNIEnv 指针指向的函数表中选择合适的 JNI 函数来操作 JVM 中的数据结构

前面示例中,访问 java.lang.String 对应的 JNI 类型 jstring 时,没有像访问基本数据类型一样直接使用,因为它在 Java 是一个引用类型,所以在本地代码中只能通过 GetStringUTFChars 这样的 JNI 函数来访问字符串的内容。

下面通过实例介绍处理jstring的一些重要函数:

String to Char

extern "C"
jstring Java_com_honjane_ndkdemo_JNIUtils_handlerString( JNIEnv* env, jobject jobj,jstring jstr){
      //utf8
      const char* chr = env->GetStringUTFChars(jstr,0);
      //unicode
      const jchar* chr1 = env->GetStringChars(jstr,0);

      jsize jlen = env->GetStringUTFLength(jstr);
      jsize jlen1 = env->GetStringLength(jstr);

       char c[120] = "你好 2017";
       //把chr拼接到c
       strcat(c,chr);

      jstring new_str = env->NewStringUTF(c);

      env->ReleaseStringUTFChars(jstr,chr);
      env->ReleaseStringChars(jstr,chr1);

    return new_str;
}

输出结果:

I/main----handlerString: 你好 2017 happy new year

  这里使用GetStringUTFChars方法将传进来的jstr(jstring类型)转换成为UTF-8的格式,就能够在本地方法中使用了。
  
  注意:在使用完你所转换之后的对象之后,需要显示调用ReleaseStringUTFChars方法,让JVM释放转换成UTF-8的string的对象的空间,如果不显示的调用的话,JVM中会一直保存该对象,不会被垃圾回收器回收,因此就会导致内存溢出。

  ◆GetStringUTFChars将jstring转换成为UTF-8格式的char*
  
  ◆GetStringChars将jstring转换成为Unicode格式的char*
  
  ◆ReleaseStringUTFChars释放指向UTF-8格式的char*的指针
  
  ◆ReleaseStringChars释放指向Unicode格式的char*的指针
  
  ◆NewStringUTF创建一个UTF-8格式的String对象
  
  ◆NewString创建一个Unicode格式的String对象
  
  ◆GetStringUTFLengt获取UTF-8格式的char*的长度
  
  ◆GetStringLength获取Unicode格式的char*的长度

上面实例都是把string转换为char类型,有些场景需要把string转换成byte

String to Byte

java代码

    public native byte[] handlerStrToByte(String text);
  byte [] bytes = jniUtils.handlerStrToByte("honjane");
  for (byte b : bytes) {
       Log.i("main---handlerStrToByte", (char)b+"");
  }

JNI实现:



char * jstringToChar(JNIEnv * env,jstring jstr){

    char* rtn = NULL;
    //等到String的class
    jclass jcls = env->FindClass("java/lang/String");
    //定义string的编码格式
    jstring strcode = env->NewStringUTF("utf-8");
    //获得string的getBytes method Id
    jmethodID jmid = env->GetMethodID(jcls, "getBytes", "(Ljava/lang/String;)[B");
    //调用getBytes方法 返回一个jbyteArray
    jbyteArray byteArr= (jbyteArray)env->CallObjectMethod(jstr, jmid, strcode);

    jsize alen = env->GetArrayLength(byteArr);
    //数组指向一个byte指针
    jbyte* pbyte = env->GetByteArrayElements(byteArr, 0);
    if (alen > 0)
    {
        //申请数组长度+1个内存空间
        rtn = (char*)malloc(alen + 1);
        //从源pbyte所指的内存地址的起始位置开始拷贝alen个字节到目标rtn所指的内存地址的起始位置中
        memcpy(rtn, pbyte, alen);
        rtn[alen] = 0;
    }
    //释放
    env->ReleaseByteArrayElements(byteArr, pbyte, 0);
    return rtn;
}


//string to byte
extern "C"
jbyteArray Java_com_honjane_ndkdemo_JNIUtils_handlerStrToByte( JNIEnv* env, jobject jobj,jstring jstr){

    char * chr = NULL;
    chr =jstringToChar(env, jstr);

    jbyteArray RtnArr = NULL;  //下面一系列操作把chr转成jbyteArray 返回出去
    RtnArr = env->NewByteArray(strlen(chr));

    env->SetByteArrayRegion(RtnArr, 0, strlen(chr), (jbyte*)chr );

   //释放chr
   if(chr) {
        free(chr);
    }

    return RtnArr;
}

输出结果:

I/main---handlerStrToByte: h
I/main---handlerStrToByte: o
I/main---handlerStrToByte: n
I/main---handlerStrToByte: j
I/main---handlerStrToByte: a
I/main---handlerStrToByte: n
I/main---handlerStrToByte: e

上面代码注释都解释的很明白了,不在多强调,这里主要介绍下SetByteArrayRegion,该函数将本地的数组数据拷贝到了 Java 端的数组中。这个函数的功能不多说主要注意Region。

GetStringRegion和GetStringUTFRegion

字符串操作也有类似的函数GetStringRegionGetStringUTFRegion,这对函数会把源字符串复制到一个预先分配的缓冲区内,将指向Java字符串的 jstring 以相应编码 截取之后 传入 C++分配出来存储字符串的空间buf。

GetStringUTFRegion与 GetStringUTFChars 比较相似,不同的是,GetStringUTFRegion 内部不分配内存,不会抛出内存溢出异常。不分配内存,不抛出内存溢出异常,这个多好的功能,会经常使用到这个函数。

注意:GetStringUTFRegion 和 GetStringRegion 这两个函数由于内部没有分配内存,所以 JNI 没有提供ReleaseStringUTFRegion 和 ReleaseStringRegion 这样的函数。

GetStringCritical

除了GetStringUTFChars,GetStringUTFRegion这2对函数之外还有一对函数GetStringCriticalReleaseStringCritical

为了增加**直接传回Java字符串的指针的可能性(不是拷贝)**JDK1.2 之后增加了这对函数。

const jchar * GetStringCritical(jstring string, jboolean *isCopy) {  
      return functions->GetStringCritical(this,string,isCopy);  
}  
void ReleaseStringCritical(jstring string, const jchar *cstring) {  
      functions->ReleaseStringCritical(this,string,cstring);  
} 

1.这两个函数之间是一个关键区,在这个关键区中不能调用JNI的其它函数或可能造成当前线程中断、等待的任何本地代码 , 否则将造成关键区代码执行期间垃圾回收器停止运作,任何触发垃圾回收器的线程也将暂停,其它触发垃圾回收器的线程不能前进,直到当前线程结束来激活垃圾回收器

2.关键区中不能出现中断操作,不能在 JVM 中分配新对象,否则造成 JVM 死锁.

3.虽然增加了直接传回Java字符串指针的可能性,不过还是需要根据实际情况传回拷贝过的字符串,如 Java 字符串是UTF-16 ,要想转成UTF-8编码还是需要进行拷贝,所以也就没有GetStringUTFCritical 这个函数

源码下载:https://github.com/honjane/JNIDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值