参考:《Java核心技术 卷II:高级特性》第12章 本地方法
##################################################################
Java编程语言中的字符串是UTF-16编码点的序列,而C的字符串则是以null结尾的字节序列,所以在这两种语言中的字符串是不一样的。Java本地接口有两组操作字符串的函数,一组把Java字符串转换成"改良的UTF-8"字节序列,另一组将它们转换成UTF-16数值的数组,也就是说转换成jchar数组。
如果你的C代码已经使用了Unicode,你可以使用第二组转换函数。
另一方面,如果你的字符串都仅限于ASCII字符,你可以使用"改良的UTF-8"转换函数
带有字符串参数的本地方法实际上都要接受一个jstring类型的值。带有字符串参数返回值的本地方法必须返回一个jstring类型的值。JNI函数将读入并构造出这些jstring对象。
例如,NewStringUTF函数从包含ASCII字符的字符数组创建一个新的jstring对象,或者是更一般的“改良的UTF-8”编码的字节序列。(使用NewStringUTF方法即可)
以下是对NewStringUTF函数的一个调用:
JNIEXPORT jstring JNICALL Java_HelloNative_getGreeting(JNIEnv *env, jclass cl) { jstring jstr; char greeting[] = "Hello, Native World!"; jstr = (*env)->NewStringUTF(env, greeting); return jstr; }
note:C++中对JNI函数的访问要简单一些。JNIEnv类的C++版本有一个内联成员函数,它负责帮你查找函数指针。故调用方式为:
jstr = env->NewStringUTF(greeting)注意,这里从该调用的参数列表里删掉了JNIEnv指针
NewStringUTF函数可以用来构造一个新的jstring,而读取现有jstring对象的内容,需要使用GetStringUTFChars函数。该函数返回指向描述字符串的"改良UTF-8"字符的const jbyte*指针。
note:具体的虚拟机可以自由地选择该编码来表示它内部的字符串。所以,你可以得到实际的Java字符串的字符指针。因为Java字符串是不可变的,所以慎重处理const就显得非常重要,不要试图将数据写到该字符数组中。
另一方面,如果虚拟机使用UTF-16或UTF-32字符作为其内部字符串的表示,那么该函数会分配一个新的内存来存储等价的“改良UTF-8”编码字符。
虚拟机必须知道何时使用字符串,这样就能进行垃圾回收(垃圾回收器是在一个独立线程中运行的,它能够终端本地方法的执行)。基于这个原因,必须调用ReleaseStringUTFChars函数。
另外,可以通过调用GetStringRegion或GetStringUTFRegion方法来提供自己的缓存,以存放字符串的字符。
函数GetStringUTFLength函数返回编码字符串所需的"改良UTF-8"字符个数。
以C代码访问Java字符串
jstring NewStringUTF(JNIEnv *env, const char bytes[])根据以全0字节结尾的“改良UTF-8”字节序列,返回一个新的Java字符串对象,或者当字符串无法构造时,返回NULL
jsize GetStringUTFLength(JNIEnv *env, jstring string)
返回进行UTF-8编码所需的字节个数(不计算作为终止符的全0字节)
const jbyte* GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy)返回"改良UTF-8"编码的字符串的指针,或者当不能构建字符数组时返回NULL。直到ReleaseStringUTFChars函数调用前,该指针一直有效。 isCopy指向一个jboolean,如果进行了复制,则填入JNI_TRUE,否则填入JNI_FALSE。
void ReleaseStringUTFChars(JNIEnv *env, jstring string, const jbyte bytes[])通知虚拟机本地代码不再需要通过bytes(GetStringUTFChars返回的指针)返回Java字符串
void GetStringRegion(JNIEnv *env, jstring string, jsize start, jsize lengh, jchar *buffer)将一个UTF-16双字节序列从字符串复制到用户提供的尺寸至少大于2xlength的缓存中。
void GetStringUTFRegion(JNIEnv *env, jstring string, jsize start, jsize length, jbyte *buffer)将一个"改良UTF-8"字符序列从字符串复制到用户提供的缓存中。为了存放要复制的字节,该缓存必须足够长。最坏情况下,要复制3xlength个字节。
jstring NewString(JNIEnv* env, const jchar chars[], jsize length)根据Unicode字符串返回一个新的Java字符对象,或者在不能创建时返回NULL
参数:env JNI接口指针
chars 以null结尾的UTF-16字符串
length 字符串中字符的个数
jsize GetStringLength(JNIEnv *env, jstring string)返回字符串中字符的个数。
const jchar* GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy)返回Unicode编码的字符串的指针,或者当不能构建字符数组时返回NULL。直到ReleaseStringChars函数调用前,该指针一直有效。isCopy为NULL或者是指向JNI_TRUE填充的jboolean,否则,它指向JNI_FALSE填充的jboolean.
void ReleaseStringChars(JNIEnv *env, jstring string, const jchar chars[])通知虚拟机本地代码不再需要通过chars(GetStringChars返回的指针)返回Java字符串。
##########################################################################################3
使用带有本地sprint方法的类
格式化浮点数的C函数原型如下:
JNIEXPORT jstring JNICALL Java_Printf2_sprint(JNIEnv *env, jclass cl, jstring format, jdouble x)
/** * @time 15-11-10 * @author zj **/ class Printf2Test { public static void main(String[] args) { double price = 44.95; double tax = 7.75; double amountDue = price * (1 + tax / 100); String s = Printf2.sprint("Amount due = %8.2f", amountDue); System.out.println(s); } }
/** * @time 15-11-10 * @author zj **/ class Printf2 { public static native String sprint(String format, double x); static { System.loadLibrary("Printf2"); } }
printf.c
/** * @time 15-11-10 * @author zj */ #include "Printf2.h" #include <stdio.h> #include <stdlib.h> #include <string.h> JNIEXPORT jstring JNICALL Java_Printf2_sprint (JNIEnv *env, jclass cl, jstring format, jdouble x) { const char* cformat; jstring ret; cformat=(*env)->GetStringUTFChars(env, format, NULL); char* cret; cret = (char*)malloc(strlen(cformat)+8); sprintf(cret, cformat, x); ret = (*env)->NewStringUTF(env, cret); free(cret); (*env)->ReleaseStringUTFChars(env, format, cformat); return ret; }