[Android的系统移植与平台开发]JNI介绍(5)
3.5 本地创建Java对象
1) 本地代码创建Java对象
JNIEnv提供了下面几个方法来创建一个Java对象:
jobject NewObject(jclass clazz, jmethodID methodID,...);
jobject NewObjectV(jclass clazz, jmethodIDmethodID,va_list args);
jobject NewObjectA(jclass clazz, jmethodID methodID,const jvalue *args) ;
本地创建Java对象的函数和前面本地调用Java方法很类似:
第一个参数jclass class 代表的你要创建哪个类的对象
第二个参数jmethodID methodID 代表你要使用哪个构造方法ID来创建这个对象。
只要有jclass和jmethodID ,我们就可以在本地方法创建这个Java类的对象。
指的一提的是:由于Java的构造方法的特点,方法名与类名一样,并且没有返回值,所以对于获得构造方法的ID的方法env->GetMethodID(clazz,method_name ,sig)中的第二个参数是固定为类名,第三个参数和要调用的构造方法有关,默认的Java构造方法没有返回值,没有参数。例如:
jclassclazz=env->FindClass("java/util/Date"); //取得java.util.Date类的jclass对象
jmethodID id_date=env->GetMethodID(clazz,"Date","()V"); //取得某一个构造方法的jmethodID
jobject date=env->NewObject(clazz,id_date); //调用NewObject方法创建java.util.Date对象
2) 本地方法对Java字符串的操作
在Java中,字符串String对象是Unicoode(UTF-16)编码,每个字符不论是中文还是英文还是符号,一个字符总是占用两个字节。在C/C++中一个字符是一个字节, C/C++中的宽字符是两个字节的。所以Java通过JNI接口可以将Java的字符串转换到C/C++的宽字符串(wchar_t*),或是传回一个UTF-8的字符串(char*)到C/C++,反过来,C/C++可以通过一个宽字符串,或是一个UTF-8编码的字符串创建一个Java端的String对象。
可以看下面的一个例子:
在Java端有一个字符串 String str="abcde";,在本地方法中取得它并且输出:
void native_string_operation (JNIEnv * env, jobject obj)
{
//取得该字符串的jfieldID
jfieldIDid_string=env->GetFieldID(env->GetObjectClass(obj), "str", "Ljava/lang/String;");
jstringstring=(jstring)(env->GetObjectField(obj, id_string)); //取得该字符串,强转为jstring类型。
printf("%s",string);
}
由上面的代码可知,从java端取得的String属性或者是方法返回值的String对象,对应在JNI中都是jstring类型,它并不是C/C++中的字符串。所以,我们需要对取得的 jstring类型的字符串进行一系列的转换,才能使用。
JNIEnv提供了一系列的方法来操作字符串:
l const jchar *GetStringChars(jstring str, jboolean*isCopy)
将一个jstring对象,转换为(UTF-16)编码的宽字符串(jchar*)。
l const char *GetStringUTFChars(jstring str,jboolean *isCopy)
将一个jstring对象,转换为(UTF-8)编码的字符串(char*)。
这两个函数的参数中,第一个参数传入一个指向Java 中String对象的jstring引用。第二个参数传入的是一个jboolean的指针,其值可以为NULL、JNI_TRUE、JNI_FLASE。
如果为JNI_TRUE则表示开辟内存,然后把Java中的String拷贝到这个内存中,然后返回指向这个内存地址的指针。
如果为JNI_FALSE,则直接返回指向Java中String的内存指针。这时不要改变这个内存中的内容,这将破坏String在Java中始终是常量的规则。
如果是NULL,则表示不关心是否拷贝字符串。
使用这两个函数取得的字符,在不适用的时候,要分别对应的使用下面两个函数来释放内存。
RealeaseStringChars(jstring jstr, const jchar*str)
RealeaseStringUTFChars(jstring jstr, constchar* str)
第一个参数指定一个jstring变量,即要释放的本地字符串的资源
第二个参数就是要释放的本地字符串
3) 创建Java String对象
jstring NewString(const jchar *unicode, jsizelen) // 根据传入的宽字符串创建一个Java String对象
jstring NewStringUTF(const char *utf) // 根据传入的UTF-8字符串创建一个Java String对象
4) 返回Java String对象的字符串长度
jsize GetStringLength(jstring jstr) //返回一个java String对象的字符串长度
jsize GetStringUTFLength(jstring jstr) //返回一个java String对象经过UTF-8编码后的字符串长度
3.6 Java数组在本地代码中的处理
我们可以使用GetFieldID获取一个Java数组变量的ID,然后用GetObjectFiled取得该数组变量到本地方法,返回值为jobject,然后我们可以强制转换为j<Type>Array类型。
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
j<Type>Array类型是JNI定义的一个对象类型,它并不是C/C++的数组,如int[]数组,double[]数组等等。所以我们要把j<Type>Array类型转换为C/C++中的数组来操作。
JNIEnv定义了一系列的方法来把一个j<Type>Array类型转换为C/C++数组或把C/C++数组转换为j<Type>Array。
jsize GetArrayLength(jarray array) // 获得数组的长度
jobjectArray NewObjectArray(jsize len, jclass clazz, jobjectinit) // 创建对象数组,指定其大小
jobject GetObjectArrayElement(jobjectArray array, jsizeindex) // 获得数组的指定元素
void SetObjectArrayElement(jobjectArray array, jsizeindex,jobject val) // 设置数组元素
jbooleanArrayNewBooleanArray(jsize len) // 创建Boolean数组,指定其大小
jbyteArrayNewByteArray(jsize len) //下面的都类似,创建对应类型的数组,并指定大小
jcharArrayNewCharArray(jsize len)
jshortArrayNewShortArray(jsize len)
jintArrayNewIntArray(jsize len)
jlongArrayNewLongArray(jsize len)
jfloatArrayNewFloatArray(jsize len)
jdoubleArrayNewDoubleArray(jsize len)
// 获得指定类型数组的元素
jboolean * GetBooleanArrayElements(jbooleanArray array,jboolean *isCopy)
jbyte * GetByteArrayElements(jbyteArray array, jboolean*isCopy)
jchar * GetCharArrayElements(jcharArray array, jboolean*isCopy)
jshort * GetShortArrayElements(jshortArray array, jboolean*isCopy)
jint * GetIntArrayElements(jintArray array, jboolean*isCopy)
jlong * GetLongArrayElements(jlongArray array, jboolean*isCopy)
jfloat * GetFloatArrayElements(jfloatArray array,jboolean *isCopy)
jdouble * GetDoubleArrayElements(jdoubleArray array,jboolean *isCopy)
// 释放指定数组
void ReleaseBooleanArrayElements(jbooleanArrayarray,jboolean *elems,jint mode)
void ReleaseByteArrayElements(jbyteArray array,jbyte*elems,jint mode)
void ReleaseCharArrayElements(jcharArray array,jchar*elems,jint mode)
void ReleaseShortArrayElements(jshortArray array,jshort*elems,jint mode)
void ReleaseIntArrayElements(jintArray array,jint*elems,jint mode)
void ReleaseLongArrayElements(jlongArray array,jlong*elems,jint mode)
void ReleaseFloatArrayElements(jfloatArray array,jfloat*elems,jint mode)
void ReleaseDoubleArrayElements(jdoubleArrayarray,jdouble *elems,jint mode)
void * GetPrimitiveArrayCritical(jarray array, jboolean*isCopy)
void ReleasePrimitiveArrayCritical(jarray array, void*carray, jint mode)
void GetBooleanArrayRegion(jbooleanArray array,jsizestart, jsize len, jboolean *buf)
void GetByteArrayRegion(jbyteArray array,jsize start,jsize len, jbyte *buf)
void GetCharArrayRegion(jcharArray array,jsize start,jsize len, jchar *buf)
void GetShortArrayRegion(jshortArray array,jsize start,jsize len, jshort *buf)
void GetIntArrayRegion(jintArray array,jsize start,jsize len, jint *buf)
void GetLongArrayRegion(jlongArray array,jsize start,jsize len, jlong *buf)
void GetFloatArrayRegion(jfloatArray array,jsize start,jsize len, jfloat *buf)
void GetDoubleArrayRegion(jdoubleArray array,jsizestart, jsize len, jdouble *buf)
void SetBooleanArrayRegion(jbooleanArray array, jsizestart, jsize len,const jboolean *buf)
void SetByteArrayRegion(jbyteArray array, jsize start,jsize len,const jbyte *buf)
void SetCharArrayRegion(jcharArray array, jsize start,jsize len,const jchar *buf)
void SetShortArrayRegion(jshortArray array, jsizestart, jsize len,const jshort *buf)
void SetIntArrayRegion(jintArray array, jsize start,jsize len,const jint *buf)
void SetLongArrayRegion(jlongArray array, jsize start,jsize len,const jlong *buf)
void SetFloatArrayRegion(jfloatArray array, jsizestart, jsize len,const jfloat *buf)
void SetDoubleArrayRegion(jdoubleArray array, jsizestart, jsize len,const jdouble *buf)