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

原创 2017年01月01日 13:43:46

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

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

JNI 字符串拼接方法

上代码: //拼接字符串的方法 JNIEXPORT jstring JNICALL Java_cn__DataProvider_sayHelloInC (JNIEnv * env , jobject...
  • u013812939
  • u013812939
  • 2015年09月06日 17:33
  • 4055

Android Studio3.0开发JNI流程------JNI中字符串拼接的三种方式(C++)

字符串拼接在java中很常见,比如拼接url路径,那么到了jni开发中怎么使用java中拼接字符串呢?不废话了啊,直接来个案例分析 我们将Android Studio3.0默认创建的工程文本显示从j...
  • cloverjf
  • cloverjf
  • 2017年12月20日 13:47
  • 167

JNI开发入门教程,实现输出一段字符串

为什么使用JNI:         效率上 C/C++是本地语言,比java更高效;         代码移植,如果之前用C语言开发过模块,可以复用已经存在的c代码;         java反编译比...
  • qq_28183203
  • qq_28183203
  • 2017年06月01日 18:40
  • 414

JNI/NDK开发指南(四)——字符串处理

JNI把Java中的所有对象当作一个C指针传递到本地方法中,这个指针指向JVM中的内部数据结构,而内部的数据结构在内存中的存储方式是不可见的。只能从JNIEnv指针指向的函数表中选择合适的JNI函数来...
  • xyang81
  • xyang81
  • 2014年12月24日 00:39
  • 14233

JNI学习二:字符串参数传递与返回值

转载请注明出处! 原文地址链接:http://blog.csdn.net/zgyulongfei/article/details/7409441 在编程的时候我们不仅需要使用无参的函数,有...
  • zgyulongfei
  • zgyulongfei
  • 2012年03月30日 00:14
  • 13859

jni开发中遇到的问题4:如何在jni中进行字符串的合并

1:现状:            jni里面没有提供方法来直接执行字符串的追加 2:思路:           a-->  先将jstring变为c string           b-->  再者...
  • Cmh_csdn
  • Cmh_csdn
  • 2017年05月31日 22:25
  • 303

三种字符串拼接方式比较

在Java中对字符串的操作可以说是最常用的,在对字符串的操作中有三种拼接字符串的方法,下面我们来看看这三种方式有何不同,在什么时候用比较合适。一、从耗时角度看先来看一段代码:package com.c...
  • lxq_xsyu
  • lxq_xsyu
  • 2014年09月12日 23:39
  • 4288

[JNI]开发之旅(8)传递参数给JNI函数

本节将介绍在JNI编程中如何传递参数和返回值。首先要强调的是,native方法不但可以传递Java的基本类型做参数,还可以传递更复杂的类型,比如String,数组,甚至自定义的类。jni.h中定义了很...
  • tsdfk1455
  • tsdfk1455
  • 2017年01月01日 00:34
  • 1194

[JNI]开发之旅(7)JNI函数中调用java对象的方法

在jni函数中我们不仅要对java对象的数据域进行访问,而且有时也需要调用java中类对象已经实现的方法。接下来我们对对象的方法调用,调用步骤与访问数据域相似。1.获得实例对应的class类 2.根据...
  • tsdfk1455
  • tsdfk1455
  • 2016年12月31日 20:51
  • 1506

c++拼接字符串效率比较(+=、append、stringstream、sprintf)

最近写的程序用到大量拼接字符串,为了提高拼接效率,比较了一下+=、append、stringstream、sprintf四种拼接字符串的方法。 测试方法比较方法是写了4个函数,分别用+=、append...
  • changqing5818
  • changqing5818
  • 2016年03月01日 18:42
  • 679
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[JNI]开发之旅(9)JNI函数字符串处理
举报原因:
原因补充:

(最多只允许输入30个字)