在前面的章节JNI/NDK开发指南之JavaVM和JNIEnv中,我们详细介绍了一番JavaVM和JNIEnv,那么今天的篇章里面分别介绍一番jobject和jclass两个概念。
在JNI开发中JNI,实例引用(jobject)和类引用(jclass)让人觉得很困惑,至少我刚开始学习的时候是这么样的。jobject与jclass通常作为JNI函数的第二个参数,也许你在看Android源码或者其它人的相关代码时,你可能会留意到为啥JNI的第二个参数为啥通常是变化的呢,有时候是jobject有时候是jclass呢?这个要看该JNI函数所对应的函数在Java类中的申明,当所声明Native方法是静态方法时,对应参数jclass,因为静态方法不依赖对象实例,而依赖于类,所以参数中传递的是一个jclass类型。相反,如果声明的Native方法时非静态方法时,那么对应参数是jobject 。
#ifdef __cplusplus
/*
* Reference types, in C++
*/
class _jobject {};
class _jclass : public _jobject {};
typedef _jobject* jobject;
typedef _jclass* jclass;
...
#else /* not __cplusplus */
typedef void* jobject;
typedef jobject jclass;
...
#endif /* not __cplusplus */
定义比较简单就不一一细说了。
实例引用和java.lang.Object类或它的子类的实例对应。类引用与java.lang.Class实例对应,它代表着类的类型。一个操作如GetFieldID,需要参数jclass,是一个类操作,因为它从一个类中获得field的描述。与此相反,GetIntField需要参数jobject,这是一个实例操作,因为它从这个实例中获得这个field的值。在所有的JNI方法中jobject和实例操作的结合和jclass和类操作的结合保持一致。所以是很容易记住类操作与实例操作的不同的。上述所描述的可以在JNI中函数体现出下,如下:
//下述函数都是类引用有关系,所以参数都是jclass
jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
//下述函数都是和实例引用有关系,所以参数都是jobject
jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
object (*GetObjectField)(JNIEnv*, jobject, jfieldID);
静态方法与静态变量一样,属于类本身,而不属于那个类的一个对象。调用一个被定义为static的方法,可以通过在它前面加上这个类的名称,也可以像调用非静态方法一样通过类对象调用。
实例方法必须通过类的实例来使用。实例方法可以使用类的非静态成员,也可以使用类的静态成员。
类的静态方法,静态变量是在类装载的时候装载的。但是要特别注意,类的静态变量是该类的对象所共有的,即是所有对象共享变量。所以建议尽量少用静态变量。尽量在静态方法中使用内部变量。
其中static关键字即表示静态的。声明静态方法的语法如下:
<访问修饰符>static返回类型 方法名(参数列表)
{//方法体}
静态方法与实例方法唯一不同的,就是静态方法在返回类型前加static关键字。静态方法的调用有两种途径:
(1) 通过类的实例对象去调用
调用格式为: 对象名.方法名
(2) 通过类名直接调用
调用格式为: 类名::方法名
通过前面的章节,我想读者朋友们一定对jobject和jclass有了清晰的认识了,下面我们以实际code举例说明。
Java端代码:
public class Jobject_Jclass {
public native static void fun();//类方法
public native void fun1();//实例方法
}
JNI端代码:
通过javah命令生成头文件,生成的头文件如下,注意这里的第二个参数,你会发现原来真的如此,诚不欺我啊。
/*
* Class: com_xxx_api_binder_Jobject_Jclass
* Method: fun
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_xxx_api_binder_Jobject_1Jclass_fun
(JNIEnv *, jclass);
/*
* Class: com_xxx_api_binder_Jobject_Jclass
* Method: fun1
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_xxx_api_binder_Jobject_1Jclass_fun1
(JNIEnv *, jobject);
好了,关于jobject和jclass到这里就要告一段落了。下面让我们再次总结一下关于jobject和jclass的区别:
当Java中定义的native方法为静态方法时,则第二个参数为jclass,jclass代表native方法所属类的class本身。
当Java中定义的native方法为非静态方法时,则第二个参数为jobject,jobject代表native方法所属类的实例对象。
最后留下一个问题,jclass和jobject可以相互转换吗,如果可以转换具体是通过什么方式呢?