android jni jclass,JNI基础之JNIEnv,jclass和jobject

在上一篇文章中,简单的介绍了eclipse下生成jni头文件以及java调用C语言的流程,其中,在生成的头文件方法声明中,需要传入一个JNIEnv类型的变量,这里我们就来看一下JNIEnv这个变量类型

JNIEXPORT jstring JNICALL Java_com_will_jni_JNITest_getStringFromC

(JNIEnv *, jclass);

JNIEnv 类型

我们右击JNIEnv,点击转到声明,跳转到jni.h头文件中JNIEnv的声明处。

#ifdef __cplusplus

typedef JNIEnv_ JNIEnv;

#else

typedef const struct JNINativeInterface_ *JNIEnv;

#endif

这里通过预编译指令分别针对C++和C语言环境JNIEnv作了不同的声明,上面的代码中可以看到,在C++环境中, JNIEnv是 JNIEnv_结构体的别名,而在C语言环境中,它是JNINativeInterface_结构体的指针别名,注意这里C++和C中声明的区别,一个是结构体的别名,一个是结构体指针的别名。为什么会有这种区别呢,我们在后面再说。

c语言中的JNIEnv

接着跳到C语言中JNINativeInterface_结构体的定义中

struct JNINativeInterface_ {

void *reserved0;

void *reserved1;

void *reserved2;

void *reserved3;

jint (JNICALL *GetVersion)(JNIEnv *env);

jclass (JNICALL *DefineClass)

(JNIEnv *env, const char *name, jobject loader, const jbyte *buf,

jsize len);

jclass (JNICALL *FindClass)

(JNIEnv *env, const char *name);

···

}

可以看到,在这个结构体中,它定义了一大堆的函数,如上面罗列的GerVersion,DefinedClass,FindClass等,仔细看完这个结构体,可以发现这个结构体中基本声明了jni.h中的所有函数。JNIEnv类型实际上代表了Java环境,通过这个JNIEnv* 指针,就可以对Java端的代码进行操作。例如,创建Java类中的对象,调用Java对象的方法,获取Java对象中的属性等等。JNIEnv的指针会被JNI传入到本地方法的实现函数中来对Java端的代码进行操作。

NewObject:创建Java类中的对象

NewString:创建Java类中的String对象

NewArray:创建类型为Type的数组对象

GetField:获取类型为Type的字段

SetField:设置类型为Type的字段的值

GetStaticField:获取类型为Type的static的字段

SetStaticField:设置类型为Type的static的字段的值

CallMethod:调用返回类型为Type的方法

CallStaticMethod:调用返回值类型为Type的static方法

2. C++中 JNIEnv 的定义

接着看C++中JNIEnv_的定义:

struct JNIEnv_ {

const struct JNINativeInterface_ *functions;

#ifdef __cplusplus

jint GetVersion() {

return functions->GetVersion(this);

}

jclass DefineClass(const char *name, jobject loader, const jbyte *buf,

jsize len) {

return functions->DefineClass(this, name, loader, buf, len);

}

jclass FindClass(const char *name) {

return functions->FindClass(this, name);

}

···

}

可以看到,C++中JNIEnv_定义了一个指针变量functions,它实际是C语言中JNINativeInterface_的指针别名,在看C++中其他函数的实现,都是通过JNINativeInterface_的实现,到这里也就明了C++和C语言中JNIEnv的区别了。

3. 一级指针VS二级指针?

回到最开始的那段代码,可以看到jni生成方法的声明中传入的是JNIEnv*的指针变量,传入的指针。

JNIEXPORT jstring JNICALL Java_com_will_jni_JNITest_getStringFromC

(JNIEnv * env, jclass jcls){

return (*env)->NewStringUTF(env, "Hellow world jni!");

}

如前面所说, JNIEnv在C++中是结构体JNIEnv_的别名,而在C中则是JNINativeInterface_指针的别名。那么在C中,传入JINEnv指针变量env,则是JNINativeInterface_的二级指针,而C++中则是JNIEnv_的一级指针。

那么为什么一个用一级指针,一个用二级指针呢?JNIEnv类型实际上代表了Java环境,函数执行的过程中需要JNIEnv作为上下文的环境变量,而C++可以获取到当前指针this。

我们通过模拟JNIEnv的NewStringUTF方法来实现这个指针的传递过程。

定义一个结构体

//结构体指针别名

typedef struct JNINativeInterface_ *JNIEnv;

//结构体定义

struct JNINativeInterface_{

char* (*NewStringUTF)(JNIEnv*, char*);

};

//函数实现

char* NewStringUTF(JNIEnv* env, char* str){

return str;

}

void main(){

//首先要实例化结构体:

struct JNINativeInterface_ struct_env;

struct_env.NewStringUTF = NewStringUTF;

//结构体指针

JNIEnv e = &struct_env;

//结构体二级指针

JNIEnv *env = &e;

char* result = (*env)->NewStringUTF(env, "Hello world!");

printf("%s\n", result);

getchar();

}

jclass和jobject类型

我们将在上面的java类中新增一个native非静态方法

public class JNITest {

public native static String getStringFromC();

public native String getStringC();

public static void main(String[] args) {

String text = getStringFromC();

System.out.println(text);

JNITest jniTest = new JNITest();

String text2 = jniTest.getStringC();

System.out.println(text2);

}

static {

System.loadLibrary("c_jni");

}

}

通过javah命令生成头文件,发现新增了一个native函数:

/*

* Class:     com_will_jni_JNITest

* Method:    getStringFromC

* Signature: ()Ljava/lang/String;

*/

JNIEXPORT jstring JNICALL Java_com_will_jni_JNITest_getStringFromC

(JNIEnv *, jclass);

/*

* Class:     com_will_jni_JNITest

* Method:    getStringC

* Signature: ()Ljava/lang/String;

*/

JNIEXPORT jstring JNICALL Java_com_will_jni_JNITest_getStringC

(JNIEnv *, jobject);

可以看到第一个函数对应的是java中的静态方法,而第二个函数则对应非静态方法。对比这两个函数的参数,发现都传了两个参数,第一个是JNIEnv*,而第二个参数在静态方法中传入的是jclass类型,而非静态方法传入的是jobject类型。实际上,为了能够在Native层访问Java中的类和对象,jobject和jclass 分别指代了其所指代的类和对象,进而访问成员方法和成员变量等。jclass和jobject的定义可在jni.h中找到:

class _jobject {};

class _jclass : public _jobject {};

···

typedef _jobject *jobject;

typedef _jclass *jclass;

···

当java中定义的native方法为静态方法时,则第二个参数为jclass,JClass代表native方法所属类的class本身。

当java中定义的native方法为非静态方法时,则第二个参数为jclass,JClass代表native方法所属类的实例对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值