JNI基础学习笔记--字符串处理

13 篇文章 0 订阅
5 篇文章 0 订阅

首页需要理解的相关概念

  1. java内部是使用16bit的unicode编码(UTF-16)来表示字符串的,无论中文英文都是2字节;
  2. jni内部是使用UTF-8编码来表示字符串的,UTF-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节;
  3. c/c++使用的是原始数据,ascii就是一个字节了,中文一般是GB2312编码,用两个字节来表示一个汉字。

在开发中jni提供了如下这些方法来对字符进行相关的操作,详细说明可以参考官网:
http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#string_operations

jstring NewString (JNIEnv *env, const jchar *unicodeChars, jsize len);
功能:利用 Unicode 字符数组构造新的 java.lang.String 对象。
参数: env:JNI 接口指针。
unicodeChars:指向 Unicode 字符串的指针。
len:Unicode 字符串的长度。
返回值: Java 字符串对象。如果无法构造该字符串,则为NULL。
抛出: OutOfMemoryError:如果系统内存不足。

jstring NewStringUTF (JNIEnv *env, const char *bytes);
功能:利用 UTF-8 字符数组构造新 java.lang.String 对象。
参数: env:JNI 接口指针。如果无法构造该字符串,则为 NULL。
bytes:指向 UTF-8 字符串的指针。
返回值:Java 字符串对象。如果无法构造该字符串,则为NULL。
抛出: OutOfMemoryError:如果系统内存不足。

jsize GetStringLength (JNIEnv *env, jstring string);
功能:返回 Java 字符串的长度(Unicode 字符数)。
参数: env:JNI 接口指针。
string:Java 字符串对象。
返回值: Java 字符串的长度。

jsize GetStringUTFLength (JNIEnv *env, jstring string);
功能:以字节为单位返回字符串的 UTF-8 长度。
参数: env:JNI 接口指针。
string:Java 字符串对象。

const jchar * GetStringChars (JNIEnv*env, jstring string, jboolean *isCopy);
功能:返回指向字符串的 Unicode 字符数组的指针。该指针在调用 ReleaseStringchars() 前一直有效。
如果 isCopy 非空,则在复制完成后将 *isCopy 设为 JNI_TRUE。如果没有复制,则设为JNI_FALSE。
参数: env:JNI 接口指针。
string:Java 字符串对象。
isCopy:指向布尔值的指针。
返回值: 指向 Unicode 字符串的指针,如果操作失败,则返回NULL。

const char* GetStringUTFChars (JNIEnv*env, jstring string, jboolean *isCopy);
功能:返回指向字符串的 UTF-8 字符数组的指针。该数组在被ReleaseStringUTFChars() 释放前将一直有效。 如果 isCopy
不是 NULL,*isCopy 在复制完成后即被设为 JNI_TRUE。如果未复制,则设为 JNI_FALSE。
参数: env:JNI 接口指针。
string:Java 字符串对象。
isCopy:指向布尔值的指针。

void ReleaseStringChars (JNIEnv *env, jstring string, const jchar *chars);
功能:通知虚拟机平台相关代码无需再访问 chars。参数chars 是一个指针,可通过 GetStringChars() 从 string 获得。
参数: env:JNI 接口指针。
string:Java 字符串对象。
chars:指向 Unicode 字符串的指针。

void ReleaseStringUTFChars (JNIEnv *env, jstring string, const char *utf);
功能:通知虚拟机平台相关代码无需再访问 utf。utf 参数是一个指针,可利用 GetStringUTFChars() 获得。
参数: env:JNI 接口指针。
string:Java 字符串对象。
utf:指向 UTF-8 字符串的指针。

void GetStringRegion(JNIEnv *env,jstring str,jsize start,jsize len, jchar *buf);
功能: 从start位置拷贝一个len长度的Unicode字符到buf中。
参数: env:JNI 接口指针。
string:Java 字符串对象。
utf:指向 Unicode 字符串的指针。

void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);
功能: 从start位置拷贝一个len长度的UTF-8字符到buf中。
参数: env:JNI 接口指针。
string:Java 字符串对象。
utf:指向 UTF-8 字符串的指针。

实际使用Demo代码如下(C语言)

#include "header/com_aidesudi_testjni_StringUtil.h"
#include "mylog.h"
#include <string.h>
JNIEXPORT jstring JNICALL Java_com_aidesudi_testjni_StringUtil_passStringReturnString
        (JNIEnv *env, jobject jobject, jstring pStr){

    // 测试直接输入Java字符串,因为编码格式的不同,这里会报错
    //LOGI("print jstring : %s",s);

    const jbyte *jstr;
    jboolean *isCopy;

    //GetStringUTFChars使用
    jstr = (*env)->GetStringUTFChars(env, pStr, isCopy);
    // 不要忘记检测,否则分配内存失败会抛出异常
    if(jstr==NULL){
        LOGI("cannot convert jstring to utf-8 string");
        //return NULL;
    }
    LOGI("print UTF-8 string :%s,%d",jstr,isCopy);

    //GetStringUTFLength使用
    jsize length = (*env)->GetStringUTFLength(env,pStr);
    LOGI("UTF-8 string length (number of bytes):%d == %d",length,strlen(jstr));
    LOGI("UTF-8 string end with :%d %d",jstr[length],jstr[length]+1);

    //ReleaseStringUTFChars使用
    (*env)-> ReleaseStringUTFChars(env,pStr,jstr);

    //GetStringUTFRegion使用
    char nativeStr[100];
    (*env)->GetStringUTFRegion(env, pStr, 0, length, nativeStr);
    LOGI("*jstring converted to UTF-8 string and copied to native buffer :%s",nativeStr);

    //NewStringUTF使用,在JNI中创建一个UTF字符串
    const char* newStr = "Test ndk 安卓";
    jstring ret = (*env)->NewStringUTF(env,newStr);
    jsize newStrlen = (*env)->GetStringUTFLength(env,ret);
    LOGI("UTF-8 string with Chinese characters :%s ,string length (number of bytes) %d=%d",newStr,newStrlen,strlen(newStr));

    return ret;
}

C++代码

#include "header/com_aidesudi_testjni_StringUtil.h"
#include "mylog.h"
#include "string.h"

JNIEXPORT jstring JNICALL Java_com_aidesudi_testjni_StringUtil_passStringReturnString
        (JNIEnv * env, jclass, jstring pStr){

    // 测试直接输入Java字符串,因为编码格式的不同,这里会报错
    //LOGI("print jstring : %s",s);

    const char *jstr;
    jboolean *isCopy;

    //GetStringUTFChars使用
    jstr = env->GetStringUTFChars(pStr, isCopy);
    // 不要忘记检测,否则分配内存失败会抛出异常
    if(jstr==NULL){
        LOGI("cannot convert jstring to utf-8 string");
        // return NULL;
    }
    LOGI("print UTF-8 string :%s,%d",jstr,isCopy);

    //GetStringUTFLength使用
    jsize length = env->GetStringUTFLength(pStr);
    LOGI("UTF-8 string length (number of bytes):%d == %d",length,strlen(jstr));
    LOGI("UTF-8 string end with :%d %d",jstr[length],jstr[length]+1);

    //ReleaseStringUTFChars使用
    env-> ReleaseStringUTFChars(pStr,jstr);

    //GetStringUTFRegion使用
    char nativeStr[100];
    env->GetStringUTFRegion(pStr, 0, length, nativeStr);
    LOGI("*jstring converted to UTF-8 string and copied to native buffer :%s",nativeStr);

    //NewStringUTF使用,在JNI中创建一个UTF字符串
    const char* newStr = "Test ndk 安卓";
    jstring ret = env->NewStringUTF(newStr);
    jsize newStrlen = env->GetStringUTFLength(ret);
    LOGI("UTF-8 string with Chinese characters :%s ,string length (number of bytes) %d=%d",newStr,newStrlen,strlen(newStr));
    return ret;
}

打印结果
这里写图片描述

关于相关方法的调用C与C++的区别可以查看jni.h 头文件。
其实区别也很简单,比如
C编程环境中使用方法为:(*env) ->NewStringUTF(env , “123”) ;
C++编程环境中则是: env ->NewStringUTF( “123”) ; 操作更加简单

小结

  1. GetStringUTFChars可以把一个 jstring指针(指向JVM内部的Unicode字符序列)转化成一个UTF-8格式的C字符串。调用完GetStringUTFChars做安全检查,因为JVM需要为新诞生的字符串分配内存空间,当内存空间不够分配的时候,会导致调用失败,失败后GetStringUTFChars会返回NULL,并抛出一个OutOfMemoryError异常。JNI的异常和Java中的异常处理流程是不一样的,Java遇到异常如果没有捕获,程序会立即停止运行。而JNI遇到未决的异常不会改变程序的运行流程,也就是程序会继续往下走,这样后面针对这个字符串的所有操作都是非常危险的,因此,我们需要用return语句跳过后面的代码,并立即结束当前方法。

  2. 关于参数 *isCopy 的说明,如下函数:
    const char* GetStringUTFChars(JNIEnv*env, jstring string, jboolean *isCopy);
    当从JNI函数GetStringUTFChars函数中返回得到字符串B时,如果B是原始字符串Java.lang.String的一份拷贝,则isCopy 被赋值为JNI_TRUE。
    如果B是和原始字符串指向的是JVM中的同一份数据,则isCopy 被赋值为JNI_FALSE。当isCopy 为JNI_FALSE时,本地代码绝不能修改字符串的内容,否则JVM中的原始字符串也会被修改,这会打破Java语言中字符串不可变的规则。
    通常,我们不必关心JVM是否会返回原始字符串的拷贝,只需要为isCopy传递NULL作为参数 。

  3. 对于小字符串来说,GetStringRegionGetStringUTFRegion这两对函数是最佳选择,因为缓冲区可以被编译器提前分配,而且永远不会产生内存溢出的异常。当你需要处理一个字符串的一部分时,使用这对函数也是不错。因为它们提供了一个开始索引和子字符串的长度值。另外,复制少量字符串的消耗也是非常小的。

  4. 使用GetStringCriticalReleaseStringCritical这对函数时,必须非常小心。 一定要确保在持有一个由 GetStringCritical 获取到的指针时,本地代码不会在 JVM 内部分配新对象,或者做任何其它可能导致系统死锁的阻塞性调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值