采得百花成蜜后,为谁辛苦为谁甜。这篇文章主要讲述#yyds干货盘点#Android C++系列:JNI调用 Java 类的构造方法和父类的方法相关的知识,希望能为你提供帮助。
?
android JNI开发时经常遇到C/C++层访问java层对象的,比如C/C++层创建一个String返回,或者访问Java层提供的MediaCodec等,此时我们就需要通过 JNI 来调用 Java 一个类的构造方法来创建这个 Java 类。
调用构造方法构造方法是特殊的类方法,但是调用构造方法和之前调用类的实例方法步骤类似,也需要获得对应的类的jclass和方法 id。
下面以 String 的字符数组构造方法为例在C/C++层实现对象的创建。?<
init>
?
? ,返回值类型是 void 。
public String(char value[])
对应的 C++ 代码:
extern "C"
JNIEXPORT jstring JNICALL
Java_com_qingkouwei_demo_InvokeDemo_invokeStringConstructors(JNIEnv *env, jobject instance)
jclass stringClass;
jmethodID cid;
jcharArray elemArr;
jstring result;
// 创建string字符串
jstring temp = env->
NewStringUTF("this is char array");
// jstring字符串转换为字节数组,作为构造Java String的参数
const jchar *chars = env->
GetStringChars(temp, NULL);
int len = 10;
stringClass = env->
FindClass("java/lang/String");
// 找到具体的 String 类
if (stringClass == NULL)
return NULL;
//容错
// 找到具体的方法,([C)V 表示选择 String 的 String(char value[]) 构造方法
cid = env->
GetMethodID(stringClass, "<
init>
", "([C)V");
if (cid == NULL)
return NULL;
//容错
// 字符串数组作为参数
elemArr = env->
NewCharArray(len);
if (elemArr == NULL)
return NULL;
// 给字符串数组赋值
env->
SetCharArrayRegion(elemArr, 0, len, chars);
// 使用NewObject创建类
result = (jstring) env->
NewObject(stringClass, cid, elemArr);
env->
DeleteLocalRef(elemArr);
env->
DeleteLocalRef(stringClass);
return result;
我们先构造好字符数组,并赋值,再获取String的jclass以及构造方法的方法id,通过NewObject传入字符数组来调用Sting 的构造函数创建String对象。
其他的不管是Android系统提供的类还是自定义的各种Java类,都可以通过上述流程来创建。
再来看一个调用自定义类的构造方法的示例,还是之前的 Animal 类,它的构造方法有一个 String 类型的参数。
NewObject其实是一个方法做了两件事情:
Jni提供了AllocObject 创建对象和CallNonvirtualVoidMethod调用构造方法来分步完成Java对象的构建,以我们自定义的一个简单类为例:
/**
* 通过 AllocObject 方法来创建一个类
*/
extern "C"
JNIEXPORT jobject JNICALL
Java_com_qingkouwei_demo_InvokeDemo_allocObjectConstructor(JNIEnv *env, jobject instance)
jclass fruitClass;
jobject result;
jmethodID mid;
// 获得对应的Fruit类
fruitClass = env->
FindClass("com/qingkouwei/demo/bean/Fruit");
if (fruitClass == NULL) //为空容错判断
return NULL;
// 获得Fruit构造方法id
mid = env->
GetMethodID(fruitClass, "<
init>
", "(Ljava/lang/String;
)V");
if (mid == NULL) //为空容错判断
return NULL;
// 构造方法的参数
jstring args = env->
NewStringUTF("creat fruit use AllocObject");
// 创建未被初始化的对象
result = env->
AllocObject(fruitClass);
if (result == NULL) //为空容错判断
return NULL;
//使用CallNonvirtualVoidMethod 方法调用类的构造方法
env->
CallNonvirtualVoidMethod(result, fruitClass, mid, args);
if (env->
ExceptionCheck()) //检测创建过程中是否有异常
env->
DeleteLocalRef(result);
return NULL;
return result;
这里实现了对象创建和初始化分离的实现,在Java中没有为我们提供的方法,在JNI层提供了。
调用父类的方法我们知道Java的一大特定是多态,类是可以继承或者被继承的,那么问题来了,在JNI中创建Java对象时如何调用父类的方法呢?
我们可以在子类中通过调用 CallNonvirtualMethod 方法来调用父类的方法。
构造一个相应的子类,然后获得父类的 类型和方法 id,根据父类方法的返回值选择调用不同的 CallNonvirtualMethod函数:
【#yyds干货盘点#Android C++系列(JNI调用 Java 类的构造方法和父类的方法)】我们实现一个在苹果中调用水鬼的getName的方法:
/**
* JNI层中创建子类并调用父类方法
*/
extern "C"
JNIEXPORT void JNICALL
Java_com_qingkouwei_demo_Demo_InvokeDemo_callSuperMethod(JNIEnv *env, jobject instance)
jclass apple_cls;
// Apple 类的类型
jmethodID apple_cid;
// Apple 类的构造方法 id
jstring apple_name;
// Apple 类的构造方法参数
jobject apple;
// 获得对应的 类
apple_cls = env->
FindClass("com/qingkouwei/demo/bean/Apple");
if (apple_cls == NULL)
return;
// 获得构造方法 id
apple_cid = env->
GetMethodID(apple_cls, "<
init>
", "(Ljava/lang/String;
)V");
if (apple_cid == NULL)
return;
// 准备构造方法的参数
apple_name = env->
NewStringUTF("this is apple name");
// 创建 Apple 类
apple = env->
NewObject(apple_cls, apple_cid, apple_name);
if (apple == NULL)
return;
//调用父类的 getName 参数
jclass fruit_cls;
// 父类的类型
jmethodID fruit_mid;
// 被调用的父类的方法 id
// 获得父类对应的类
fruit_cls = env->
FindClass("com/qingkouwei/demo/bean/Fruit");
if (fruit_cls == NULL)
return;
// 获得父类被调用的方法 id
fruit_mid = env->
GetMethodID(fruit_cls, "getName", "()Ljava/lang/String;
");
if (fruit_mid == NULL)
return;
jstring name = (jstring) env->
CallNonvirtualObjectMethod(apple, fruit_cls, fruit_mid);
if (name == NULL)
return;
//print
LOGI("getName method value is %s", env->
GetStringUTFChars(name, NULL));
Apple作为Fruit的子类,首先通过NewObject 构建子类Apple对象,然后再获取父类Fruit的jclass以及方法id,通过CallNonvirtualObjectMethod调用父类Fruit的getName方法。
小结本文讲解了JNI层创建Java层对象的两种方法(通过NewObject一次性创建和通过AllocObject 和CallNonvirtualVoidMethod分布创建的方法)和如何在JNI层调用Java层类对象的父类方法的方法。对一些复杂的项目使用JNI特性提供了一些思路。
推荐阅读
- #yyds干货盘点# Kubernetes 怎样控制业务的资源水位((16))
- kafka常见问题#yyds干货盘点#
- 前端SSR的落地实践
- #yyds干货盘点#netty系列之:netty中各不同种类的channel详解
- #yyds干货盘点#Git学习-分支在实际开发流程中的应用
- Azure Virtual Desktop 实战部署之自定义域及AAD Connect准备
- 北亚数据恢复sqlserver数据库被加密无法使用,MDFLDFlog文件名称被修改的数据恢复案例
- 从JVM堆内存分析验证深浅拷贝#yyds干货盘点#
- 2种图像增强方法(图像点运算和图像灰度化处理)