第四讲
1)在C/C++本地代码中创建Java的对象
2)在C/C++本地代码中访问Java的String字符串对象
3)在C/C++本地代码中创建Java的String字符串对象
一、Java对象的创建:NewObject
1)使用函数NewObject可以创建Java对象
2)GetMethodID能够取得构造方法的jmethodID,如果传入的要取得的方法名称设定为"<init>"就能够取得构造方法。
3)构造方法的方法返回值类型的签名始终未Void
例子:
jclass class_date = env->FindClass("java/util/Date");
jmethodID mid_date = env->GetMethodID(class_date, "<init>", "()V");
jobject now = env->NewObject(class_date, mid_date);
示例代码:
static {
System.loadLibrary("jnitest1");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("tag", "xxx = " + testShow());
}
public static native String testShow();
#include <string.h>
#include <iostream>
#include <com_example_jnitest1_MainActivity.h>
JNIEXPORT void JNICALL Java_com_example_jnitest1_MainActivity_testShow (JNIEnv * env, jobject obj){
jclass class_date = env->FindClass("java/util/Date");
jmethodID mid_date = env->GetMethodID(class_date, "<init>", "()V");
jobject now = env->NewObject(class_date, mid_date);
jmethodID mid_date_getTime = env->GetMethodID(class_date, "getTime", "()J");
jlong time = env->CallLongMethod(now, mid_date_getTime);
cout << time << endl;
}
二、Java对象的创建:AllocObject
使用函数AllocObject可以根据传入的jclass创建一个Java对象,但是它的状态是非初始化的,在使用这个对象之前绝对要用CallNonvirtualVoidMethod来调用该jclass的构造函数,这样可以延迟构造函数的调用。这一个部分用的很少,只做简单的说明。
jclass class_str = env->FindClass("java/lang/String");
jmethodID methodID_str = env->GetMethodID(class_str, "<init>", "([C)V");
//先创建一个没有初始化的字串
jobject string = env->AllocObject(class_str);
// 创建一个4个元素的字符数组,然后以‘请’,‘原’,‘也’,‘卓’赋值。
jcharArray agr = env->NewCharArray(4);
env->SetCharArrayRegion(arg, 0, 4, L"请原也卓");
// 呼叫构建子
env->CallNonvirtualVoidMethod(string, class_str, methodID_str, arg);
jclass class_this = env->GetObjcetClass(obj);
// 这里假设这个对象的类中有定义 static String STATIC_STR;
jfieldID fieldID_str = env->GetStaticFieldID(class_this, "STATIC_STR", "Ljava/lang/String;");
env->SetStaticObjectField(class_str, fieldID_str, string);
三、Java字符串<-->C/C++的字符串
1)在Java中,使用的字符串String对象时Unicode(UTF-16)码,即每个字符不论是中文还是英文还是符号,一个字符总是占用两个字节。
2)Java通过JNI接口可以将Java的字符串转换到C/C++中的宽字符串(wchar_t*),或者传回一个UTF-8的字符串(char*)到C/C++。反过来,C/C++可以通过一个宽字符串,或是一个UTF-8编码的字符串来创建一个Java端的String对象。
GetStringChars
GetStringUTFChars
1)这2个函数用来取得与某个jstring对象相关的Java字符串,分别可以取得UTF-16编码的宽字符串(jchar*)跟UTF-8编码的字符串(char*)。
2)const jchar* GetStringChars(jstring str, jboolean* copied)
const char* GetStringUTFChars(jstring str, jboolean* copied)
第一个参数传入一个指向Java中的String对象的jstring变量
第二个参数传入的是一个jboolean的指针。
3)这两个函数分别都会有两个不同的动作
1.开新内存,然后把Java中的String拷贝到这个内存中,然后返回指向这个内存地址的指针。
2.直接返回指向Java中String的内存的指针,这个时候千万不要改变这个内存的内容,这将破坏String在Java中始终是常量这个原则。
4)第二个参数是用来标示是否对Java的String对象进行了拷贝的。
如果传入的这个jboolean指针不是NULL,则他会给该指针所指向的内存传入JNI)TRUE或JNI_FALSE标示是否进行了拷贝。
传入NULL标示不关心是否拷贝字符串,它就不会给jboolean*指向的内存赋值。
5)使用这两个函数取得的字串,在不使用的时候,要使用ReleaseStringChars/ReleaseStringUTFChars来释放拷贝的内存,或是释放对Java的String对象的引用。
ReleaseStringCharas(jstring jstr, const jchar* str);
ReleaseStringUTFChars(jstring jstr, const char* str);
第一个参数指定一个jstring变量,即是要释放的本地字符串的来源
第二个参数就是要释放的本地字符串
四、GetStringCritical
1)为了增加直接传回指向Java字符串的指针的可能性(而不是拷贝),JDK 1.2出来了新的函数GetStringCritical/ReleaseStringCritical。
2)const jchar* GetStringCritical(jstring str, jboolean* copied)
void ReleaseStringCritical(jstring jstr, const jchar* str);
3)在GetStringCritical/ReleaseStringCritical之间是一个关键区,在这关键区之中绝对不能呼叫JNI的其它函数和会造成当前线程中断或者是会让当前线程等待的任何本地代码。否则将造成关键区代码执行期间垃圾回收器停止运作,任何处罚垃圾回收器的线程也会暂停。其它的触发垃圾回收器不能前进直到当前线程结束而激活垃圾回收器。
4)在关键区中千万不要出现中断操作,或是在JVM中分配任何新对象,否则会造成JVM死锁。
5)虽说这个函数会增加直接传回指向Java字符串的指针的可能性,不过还是回根据情况传回拷贝过的字符串。
6)不支持GetStringUTFCritical,没有这样的函数,由于Java字符串用的是UTF16,要转成UTF8编码的字符串始终需要进行一次拷贝,所以没有这样的函数。
五、GetStringRegion, GetStringUTFRegion
1)java1.2出来的函数,这个函数的动作,是把Java字符串的内容直接拷贝到C/C++的字符数组中,在呼叫这个函数之前必须有一个C/C++分配出来的字符串,然后传入到这个函数中进行字符串的拷贝。
2)由于C/C++中分配内存开销相对小,而且Java中的String内容拷贝的开销可以忽略,更好的一点是此函数不分配内存,不会抛出OutOfMemoryError异常。
3)示例
// 拷贝Java字符串并以UTF-8转码传入buffer
GetStringUTFRegion(jstring str, jsize start, jsize len, char* buffer);
// 拷贝Java字符串并以UTF-16编码传入buffer
GetStringRegion(jstring str, jsize start, jsize len, jchar* buffer);
env->GetStringRegion(str, 3, 10, buffer);