|
签名的问题解决了,现在来看实现(例13.21)。
例13.21 ObjectImp.c。
#include <stdio.h>
#include <jni.h>
#include "ObjectDemo.h"
JNIEXPORT void JNICALL Java_ObjectDemo_display
(JNIEnv *env,jobject obj,jobject demo){
jclass clazz=(*env)->GetObjectClass(env,demo);
jfieldID id=(*env)->GetFieldID(env,clazz,"message","Ljava/lang/String;" ;
jstring js=(*env)->GetObjectField(env,demo,id);
const char *s=(*env)->GetStringUTFChars(env,js,0);
printf("The message is %s\n",s);
(*env)->ReleaseStringUTFChars(env,js,s);
printf("And the length is %d\n",(*env)->getStringLength(env,js));
printf("The length of c string is %d",strlen(s));
}
这个程序重点说明两个问题:如何访问对象的域;如何操作字符串。
程序的第6~8行是取对象域的过程。访问一个对象的域,需经过这样一个过程:
(1)得到域的ID号。JNI为此提供了专门的函数,以便根据该域所在的类、域的名字和类型签名得到ID号。ID号在类被卸载(unload)前一直保持有效。
为了做到这一点,JNI提供了一些函数:
☆ jfieldID GetFieldID(JNIEnv *env,jclass clazz,const char *name,const char *sig);
得到static型域变量的ID。
为了得到clazz的值,往往还需要调用一个函数,用来从对象得到对应的类:
☆ jclass GetObjectClass(JNIEnv *env,jobject obj);
例中的第6行用到了它。
(2)访问该域。对于static域,需要把类作为参数之一,否则用对象作为参数之一。有这样一些函数:
☆ <原生方法中的类型>Get<类型*>Field(JNIEnv *env,jobject obj,jfieldID fieldID);
这也是一族方法,其中的“<类型*>”可以为Object或表13.2中的八种基本类型(前八行)。返回值则是相应的原生方法中的类型(见表13.2)。如:
jint GetInfField(JNIEnv *env,jobject obj,jfieldID fieldID);
读一个整形域。
☆ void Set<类型*>Field(JNIEnv *env,jobject obj,jfieldID fieldID,<原生方法中的类型> value);
设置非static型的值。<类型*>、<原生方法中的类型>含义同上。
☆<原生方法中的类型> GetStatic<类型*>Field(JNIEnv *env,jclass clazz,jfieldID fieldID);
读取static型域的值。clazz是域所在的类。
☆ Void Set<类型*>StaticField(JINEnv *env,jclass calzz,jfieldID fieldID,<原生方法中的类型>value)
设置static型域的值。
读者可以根据上述过程看一下例子的6~8行,验证一下。
例子的9、11、12行是对字符串的操作。注意,在第8行得到的jstring变量不可直接输出,否则有可能引起虚拟机崩溃。因为Java虚拟机用的字符串是一种特殊的UTF-8字符串,它可以用一个、两个、三个字节来表示信息,而以字节的若干位来标识该信息由几个字节组成。它不同于C中的字符串char*。使用字符串时,必须用JNI提供的函数把它转换成C的字符串格式。
字符串操作的常用函数如下:
☆jstring NewString(JNIEnv *env,const jchar *unicodeChars,jsize len);
从一个Unicode数组创建一个java.lang.String对象。unicodeChars是指向Unicode数组的指针。
☆jsize GetStringLength(JNIEnv *env,jstring string);
返回字符串长度。
☆const jchar *GetStringChars(JNIEnv *env,jstring string,jboolean *isCopy);
返回一个指向Unicode字符数组的指针,该字符数组由组成字符串的字符组成。像访问一般数组一样,这个指针在执行对应的Release方法(这里是ReleaseStringChars())前保持有效。isCopy决定指针指向的字符数组是否经过拷贝得来。
☆ void ReleaseStringchars(JNIEnv *env,jstring string,const jchar *chars);
通知虚拟机不再需访问该字符串。chars是由GetStringChars()函数得到的。
☆ jstring NewStringUTF(JNIEnv *env,const char *bytes,jsize length);
由一个UTF-8字符组成的数组创建一个java.lang.String对象。bytes是指向UTF-8数组的打针。
☆jsize GetStringUTFLength(JNIEnv *env,jstring string);
返回string的长度(按UTF-8格式计算的字节数)。
☆jbyte *GetStringUTFChars(JNIEnv *env,jstring string jboolean *isCopy);
返回一个指向UTF-8字符数组的指针,该字符数组由组成字符串的字符组成。这个指针在执行对应的Release方法(这里是ReleaseStringUTFChars())前保持有效。isCopy决定指针指向的字符数组是否经过拷贝得来。当你确知串仅由7位的ASCII字符组成时,可以直接把它传递给C的函数,就像例子中做的那样(第9、10行)。
☆ void ReleaseStringUTFChars(JNIEnv *env,jstring string,const jbyte *utf);
通知虚拟机不再需要访问字符串string。utf由GetStringUTFChars()方法得来。
一般说来,在原生方法中访问字符串都必须经过获得指针、释放指针两步(见示例第9、11行),节不可直接输出未经转换的字符串,以免造成虚拟机崩溃。 |
|