JNI字符串操作

在Java中,创建一个 String 对象的方式非常简单,而且可以使用 String 类的各种方法很方便的操作字符串。然而在JNI中,虽然 jstring 类表示Java的 String 类,但是 jstring 并没有提供任何函数来操作字符串。

编码格式

对于一个字符串常量,例如 "abc" ,在Java中是用一个 String 对象表示,并且代表 UTF-16 编码格式的字符串(也就是双字节编码)。而在JNI中,是用一个 char 类型指针来表示,编码格式却为 modified UTF-8

那么什么是 modified UTF-8 编码格式呢?其实它与 UTF-8 格式非常像,但是有点小小的差别

  • 空字符(值为0),在 UTF-8 中是用一个字节表示,然而在 modified UTF-8 中,是用两个字节表示。
  • 对于一个字节,两个字节,三个字节可以表示的字符,这两种编码是一样的
  • 对于需要用四个字节表示的字符, modified。 UTF-8 使用的是两个三字节来实现的。

如果想了解 UTF-8modified UTF-8 编码格式,可以参考文末链接。

JNI中对字符串的操作的函数,都有针对这两个格式的版本。下面我对两种编码格式的字符串操作的函数做一个概括性的讲解。

UTF-16字符串函数

NewString

jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
复制代码

参数

  • jchar *unicodeChars : 指向 Unicode 编码字符串的指针。
  • jsize len : 字符串的长度。

NewString 使用 Unicode 编码的字符数组创建一个Java的 String 对象。

使用 NewString 的关键就是必须使用 Unicode 编码的字符串,而这里的 Unicode 编码一般都是指 UTF-16 编码。但是如何获取这个 UTF-16 编码的字符串呢,这个超出本文讨论的范畴,但是在Android开发中,有一个 String16 的类,通过构造函数就可以把一个字符串常量转化为 UTF-16 编码的字符串。

GetStringLength

jsize GetStringLength(JNIEnv *env, jstring string);
复制代码

参数

  • jstring string : Java的 String 对象。

GetStringLength 函数返回的字符串长度与Java的 String 返回的字符串的长度的是一样的,例如 String s = "中国"; ,这个Java字符串长度为2,又例如 String s = "China"; ,这个字符串长度为5。

GetStringChars

const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
复制代码

参数

  • jstring string : Java的 String 对象
  • jboolean * isCopy : 如果 isCopy 不为 NULL ,并且函数生成的是字符串的拷贝,那么 *isCopy 的值为 JNI_TRUE ,如果没有生成拷贝,那么 *isCopy 值为 JNI_FALSE

GetStringChars 返回一个指向 Unicode 编码的字符数组的指针(指针可能为 NULL )。这个指针可能指向原字符串的数组,也可能指向拷贝的字符数组,取决与虚拟机的实现。如果是生成拷贝,就需要释放本地字符串,需要使用 ReleaseStringChars

ReleaseStringChars

void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
复制代码

参数

  • jstring string : Java的 String 对象
  • jchar *chars : 指向由 GetStringChars 返回的 Unicode 编码的字符数组

ReleaseStringChars 函数并不是自己去释放本地字符串(如果发生拷贝),而是通知虚拟机本地代码不再访问 jchar *chars ,然后虚拟机自己决定如何处理。

GetStringRegion

void GetStringRegion(JNIEnv *env, jstring str, jsize start, 
                    jsize len, jchar *buf);
复制代码

参数

  • jstring str : Java的 String 对象
  • jsize start : 拷贝的开始位置
  • jsize len : 拷贝的长度
  • jchar *buf : 拷贝的目标缓冲区

GetStringRegion 函数从 start 开始,拷贝 len 长度的 Unicode 字符到 buf 中。

GetStringCritical & ReleaseStringCritical

const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);
复制代码

Get/ReleaseStringCritical 函数与 Get/ReleaseStringChars 函数在使用和功能上是一样的。也就是说, GetStringCritical 函数可能返回一个指向原字符串的指针,或者返回一个指向源字符串的拷贝的指针,这取决与虚拟机实现。如果发生了拷贝,那么就需要 ReleaseStringCritial 来通知虚拟机本地进行释放操作。

modified UTF-8字符串函数

NewStringUTF

jstring NewStringUTF(JNIEnv *env, const char *bytes);
复制代码

NewStringUTF 的第二个参数 const char *bytesmodified UTF-8 编码的字节数组。

modified UTF-8 是JNI特有的格式,使用这种格式的字符串与虚拟机中使用的字符串一样。

JNI使用 modified UTF-8 编码来表示各种字符串类型,例如下面两行代码都是使用这种编码的字符串

const char * bytes1 = "中国";
const char * bytes2 = "China";
复制代码

由于创建的字符串常量默认就是 modified UTF-8 编码的,因此 NewStringUTF 也就是大家最常用的来获取Java的 String 对象的函数。

GetStringUTFLength

jsize GetStringUTFLength(JNIEnv *env, jstring string);
复制代码

参数

  • jstring string : Java的 String 对象

GetStringUTFLength 函数返回一个 modified UTF-8 编码格式字符串的字节长度。

注意, GetStringUTFLength 返回的是字节长度,而 GetStringLength 返回的字符长度。一个是强调字节,一个是强调字符,这是有却别的。例如对于一个Java的字符串 String s = "中国"GetStringUTFLength 返回的值为6,代表6个字节长度,而 GetStringLength 返回的值为2,代表2个字符长度。

GetStringUTFChars

const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
复制代码

参数

  • jstring string : Java的String对象
  • jboolean * isCopy : 如果 isCopy 不为 NULL ,并且如果函数对原字符串进行了拷贝,那么 *isCopy 的值为 JNI_TRUE ,如果没有发生拷贝,那么 *isCopy 的值为 JNI_FALSE

GetStringUTFChars 函数返回一个指向 modified UTF-8 编码的字节数组指针。

从参数 isCopy 可以看出, GetStringUTFChars 函数返回的指向字节数组的指针,可能指向远字符串,也可能指向拷贝的字符串。如果一旦发生了拷贝,那么在不需要这个拷贝的时候,就需要进行释放,可以调用 ReleaseStringUTFChars 函数。

ReleaseStringUTFChars

void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);
复制代码

参数

  • jstring string : Java的 String 对象
  • const char *utf : 由 GetStringUTFChars 获取。

ReleaseStringUTFChars 函数只是通知虚拟机,本地代码不再访问 const char *utf ,之后的处理动作取决与虚拟机。

GetStringUTFRegion

void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, 
                        jsize len, char *buf);
复制代码

GetStringUTFRegion 函数与 GetStringRegion 函数的参数是一样,不同的是 GetStringUTFRegion 函数会把拷贝到缓冲区的字符从 Unicode 转化为 modified UTF-8 格式。

使用

一般情况下,我们都选择使用JNI默认的 modified UTF-8 编码字符串,因为很方便,不需要转换编码。而如果一定要使用 UTF-16 编码字符串,那么就需要进行转换。

那么如何在JNI层更好的处理字符串呢?如果使用的是C++开发JNI,那么可以在JNI层使用 string 类来操作字符串,而如果使用C语言,那么不得不使用字符指针一步一步来处理。

例如,一个Java类中有一个 native 方法

public class Hello
{
    native string getHelloFromJNI(String name);
}
复制代码

在JNI层对应的实现为

static jstring getHello(JNIEnv *env, jobject thiz, jstring name)
{
    // 从Java的String对象转换为本地表示
    const char *c_name = env->GetStringUTFChars(name, NULL);
    // 使用C++的string进行字符串拼接
    string str("Hello, ");
    str.append(c_name);
    str.append("!");
    env->ReleaseStringUTFChars(name, c_name);
    // 创建Java的String对象并返回
    return env->NewStringUTF(str.c_str());
}
复制代码
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章