java jni 入门3 - 字符串参数

参考:《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;
}

所有对JNI函数的调用都使用到了env指针,该指针是每一个本地方法的第一个参数。env指针是函数指针表的指针。所以,必须在每个JNI调用前面加上(*env)->,以便实际上取消对函数指针的引用。而且,env是每个JNI函数的第一个参数。

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)

printf2Test.java

/**
 * @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);
	}
}

Printf2.java

/**
 * @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;
}

注意,通过调用GetStringUTFChars来读取格式参数,通过调用NewStringUTF来产生返回值,通过调用ReleaseStringUTFChars来通知虚拟机不再需要访问字符串。





你可能感兴趣的:(java,String,jni)