Android 通过 JNI 调用 Java 类的构造方法和父类的方法
Android 还可以通过 JNI 来调用 Java 一个类的构造方法,从而创建一个 Java 类。
调用构造方法
调用构造方法的步骤和之前调用类的实例方法步骤类似,也需要获得对应的类和方法 id。
对于类,通过 FindClass 可以找到对应的 Java 类型。
对于构造方法,它的方法 id 还是通过
GetMethodID
方法来获得,但是构造方法对应的名称为 ,返回值类型是 void 类型的。完成了以上准备条件后,就可以通过
NewObject
来调用构造方法,从而创建具体的类。下面以 String 的某个构造方法为例
1publicString(char value[])// Java String 类的其中一个构造方法
2
对应的 C++ 代码:
1extern "C"
2JNIEXPORT jstring JNICALL
3Java_com_glumes_cppso_jnioperations_InvokeConstructorOps_invokeStringConstructors(JNIEnv *env, jobject instance) {
4
5 jclass stringClass;
6 jmethodID cid;
7 jcharArray elemArr;
8 jstring result;
9
10// 由 C++ 字符串创建一个 Java 字符串
11 jstring temp = env->NewStringUTF("this is char array");
12// 再从 Java 字符串获得一个字符数组指针,作为 String 构造函数的参数
13const jchar *chars = env->GetStringChars(temp, NULL);
14 int len = 10;
15
16 stringClass = env->FindClass("java/lang/String"); // 找到具体的 String 类
17if (stringClass == NULL) {
18returnNULL;
19 }
20// 找到具体的方法,([C)V 表示选择 String 的 String(char value[]) 构造方法
21 cid = env->GetMethodID(stringClass, "<init>", "([C)V");
22if (cid == NULL) {
23returnNULL;
24 }
25// 字符串数组作为参数
26 elemArr = env->NewCharArray(len);
27if (elemArr == NULL) {
28returnNULL;
29 }
30// 给字符串数组赋值
31 env->SetCharArrayRegion(elemArr, 0, len, chars);
32// 创建类
33 result = (jstring) env->NewObject(stringClass, cid, elemArr);
34 env->DeleteLocalRef(elemArr);
35 env->DeleteLocalRef(stringClass);
36return result;
37}
由于 String 的构造函数需要传递一个字符数组,就先构造好了字符数组并赋值,得到对应的类和方法 id 之后,直接通过
NewObject
方法调用即可。再来看一个调用自定义类的构造方法的示例,还是之前的 Animal 类,它的构造方法有一个 String 类型的参数。
1/**
2 * 创建一个 Java 的 Animal 类并返回
3 */
4extern"C"
5JNIEXPORT jobject JNICALL
6Java_com_glumes_cppso_jnioperations_InvokeConstructorOps_invokeAnimalConstructors(JNIEnv *env, jobject instance){
7 jclass animalClass;
8 jmethodID mid;
9 jobject result;
10 animalClass = env->FindClass("com/glumes/cppso/model/Animal");
11if (animalClass == NULL) {
12returnNULL;
13 }
14 mid = env->GetMethodID(animalClass, "<init>", "(Ljava/lang/String;)V");
15if (mid == NULL) {
16returnNULL;
17 }
18 jstring args = env->NewStringUTF("this animal name");
19 result = env->NewObject(animalClass, mid, args);
20 env->DeleteLocalRef(animalClass);
21return result;
22}
可以看到,整个调用流程只要按照步骤来,就可以了。
除了 NewObject 方法之外,JNI 还提供了 AllocObject 方法来创建对象,以同样调用 Animal 类构造方法为例:
1/**
2 * 通过 AllocObject 方法来创建一个类
3 */
4extern"C"
5JNIEXPORT jobject JNICALL
6Java_com_glumes_cppso_jnioperations_InvokeConstructorOps_allocObjectConstructor(JNIEnv *env, jobject instance){
7 jclass animalClass;
8 jobject result;
9 jmethodID mid;
10// 获得对应的 类
11 animalClass = env->FindClass("com/glumes/cppso/model/Animal");
12if (animalClass == NULL) {
13returnNULL;
14 }
15// 获得构造方法 id
16 mid = env->GetMethodID(animalClass, "<init>", "(Ljava/lang/String;)V");
17if (mid == NULL) {
18returnNULL;
19 }
20// 构造方法的参数
21 jstring args = env->NewStringUTF("use AllocObject");
22// 创建对象,此时创建的对象未初始化的对象
23 result = env->AllocObject(animalClass);
24if (result == NULL) {
25returnNULL;
26 }
27// 调用 CallNonvirtualVoidMethod 方法去调用类的构造方法
28 env->CallNonvirtualVoidMethod(result, animalClass, mid, args);
29if (env->ExceptionCheck()) {
30 env->DeleteLocalRef(result);
31returnNULL;
32 }
33return result;
34}
同样的,要先准备必要的东西。获得对应类的类型、方法 id、构造方法的参数。
然后通过
AllocObject
方法创建对象,但要注意的是,此时创建的对象是未被初始化的,不同于 NewObject
方法创建的对象直接就是初始化了,在一定程度上,可以说 AllocObject
方法是延迟初始化的。接下来是要通过
CallNonvirtualVoidMethod
来调用对应的构造方法。此处传入的一个参数不再是 jclass 类型,而是创建的未被初始化的类 jobject 。通过这种方法,同样可以创建一个 Java 中的类。
调用父类的方法
可以通过 JNI 来调用父类的实例方法。
在子类中通过调用
CallNonvirtual<Type>Method
方法来调用父类的方法。首先,构造一个相应的子类,然后获得父类的 类型和方法 id,以及准备对应的参数,根据父类方法的返回值选择调用不同的
CallNonvirtual<Type>Method
函数。对于引用类型的,调用 CallNonvirtualObjectMethod 方法;对于基础类型的,调用 CallNonvirtualBooleanMethod、CallNonvirtualIntMethod 等等;对于无返回值类型的,调用 CallNonvirtualVoidMethod 方法。
具体看代码:
1/**
2 * 调用父类的方法
3 * 创建一个子类,由子类去调用父类的方法
4 */
5extern"C"
6JNIEXPORT void JNICALL
7Java_com_glumes_cppso_jnioperations_InvokeConstructorOps_callSuperMethod(JNIEnv *env, jobject instance){
8 jclass cat_cls; // Cat 类的类型
9 jmethodID cat_cid; // Cat 类的构造方法 id
10 jstring cat_name; // Cat 类的构造方法参数
11 jobject cat;
12// 获得对应的 类
13 cat_cls = env->FindClass("com/glumes/cppso/model/Cat");
14if (cat_cls == NULL) {
15return;
16 }
17// 获得构造方法 id
18 cat_cid = env->GetMethodID(cat_cls, "<init>", "(Ljava/lang/String;)V");
19if (cat_cid == NULL) {
20return;
21 }
22// 准备构造方法的参数
23 cat_name = env->NewStringUTF("this is cat name");
24// 创建 Cat 类
25 cat = env->NewObject(cat_cls, cat_cid, cat_name);
26if (cat == NULL) {
27return;
28 }
29//调用父类的 getName 参数
30 jclass animal_cls; // 父类的类型
31 jmethodID animal_mid; // 被调用的父类的方法 id
32// 获得父类对应的类
33 animal_cls = env->FindClass("com/glumes/cppso/model/Animal");
34if (animal_cls == NULL) {
35return;
36 }
37// 获得父类被调用的方法 id
38 animal_mid = env->GetMethodID(animal_cls, "getName", "()Ljava/lang/String;");
39if (animal_mid == NULL) {
40return;
41 }
42 jstring name = (jstring) env->CallNonvirtualObjectMethod(cat, animal_cls, animal_mid);
43if (name == NULL) {
44return;
45 }
46 LOGD("getName method value is %s", env->GetStringUTFChars(name, NULL));
47
48// 调用父类的其他方法
49 animal_mid = env->GetMethodID(animal_cls, "callInstanceMethod", "(I)V");
50if (animal_mid == NULL) {
51return;
52 }
53 env->CallNonvirtualVoidMethod(cat, animal_cls, animal_mid);
54}
Cat 类作为 Animal 类的子类,首先由 NewObject 方法创建 Cat 类,然后调用它的父类的方法。
由此,通过 JNI 来调用 Java 算是基本完成了。
具体示例代码可参考我的 Github 项目,欢迎 Star。
https://github.com/glumes/AndroidDevWithCpp
欢迎关注微信公众号:【纸上浅谈】,获得最新文章推送~~
最新评论
推荐文章
作者最新文章
你可能感兴趣的文章
Copyright Disclaimer: The copyright of contents (including texts, images, videos and audios) posted above belong to the User who shared or the third-party website which the User shared from. If you found your copyright have been infringed, please send a DMCA takedown notice to [email protected]. For more detail of the source, please click on the button "Read Original Post" below. For other communications, please send to [email protected].
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。