天天看点

《Android的设计与实现:卷I》——第2章 2.3 JNI总管:JNIEnv

2.3 jni总管:jnienv

在log系统的实例中,jni层实现方法和注册方法中都使用了jnienv这个指针,通过它调用jni函数,访问java虚拟机,进而操作java对象。jnienv是jni编程中最重要的概念,本节将详细介绍它。首先看jnienv的体系结构,如图2-2所示。

《Android的设计与实现:卷I》——第2章 2.3 JNI总管:JNIEnv

在图2-2中可以看到,jnienv首先指向一个线程相关的结构,该结构又指向一个指针数组,在这个指针数组中的每个元素最终指向一个jni函数。所以可以通过jnienv去调用jni函数。

打开jni.h文件看看这部分内容是如何设计的。由于不同平台上有不同的jni.h文件,只需要取一个加以分析,这里打开libnativehelper/include/nativehelper/jni.h。

在jni.h中,为了兼容c和c++两种代码,使用宏__cplusplus加以区分。

首先看jnienv在文件中是如何定义的:

《Android的设计与实现:卷I》——第2章 2.3 JNI总管:JNIEnv

这里仅仅是用typedef关键字做了类型定义。那么_jnienv和jninativeinterface又是什么类型呢?_jnienv结构体的源码如下:

以上是对 const struct jninativeinterface类型的包装,并间接调用了const struct jninativeinterface 上定义的方法。继续分析jninativeinterface 的定义,代码如下:

struct jninativeinterface {

……

jclass (findclass)(jnienv, const char);

jint (thrownew)(jnienv , jclass, const char );

这里才真正涉及jni函数的调用。当然,这里也只是个接口,具体的实现要参考虚拟机实现。

最终可以得到如下结论:

c++中:jnienv就是struct _jnienv。 jnienv env等价于struct _jnienvenv,在调用jni函数的时候,只需要env-> findclass(jnienv, const char),就会间接调用jninativeinterface结构体里定义的函数指针,而无需首先对env解引用。

c中:jnienv就是const struct jninativeinterface。jnienv env实际等价于const struct jninativeinterface? env,因此要得到jninativeinterface结构体内的函数指针就必须先对env解引用得到(env),即const struct jninativeinterface,这个指针才是真正指向jninativeinterface结构体的指针,然后再通过它调用具体的jni函数。因此需要这样调用:(env)-> findclass(jnienv, const char)。

注意 jnienv只在当前线程中有效。本地方法不能将jnienv从一个线程传递到另一个线程中。相同的 java 线程中对本地方法多次调用时,传递给该本地方法的jnienv是相同的。但是,一个本地方法可被不同的 java 线程所调用,因此可以接受不同的 jnienv。