JNI的实现可涉及两个关键类:JNIEnv和JavaVM。
- JavaVM:这个代表java的虚拟机。所有的工作都是从获取虚拟机的接口开始的。
第一种方式,在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数)。第一个参数会传入JavaVM指针。
第二种方式,在native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)可以得到JavaVM指针。
两种情况下,都可以用全局变量,比如JavaVM* g_jvm来保存获得的指针以便在任意上下文中使用。
Android系统是利用第二种方式Invocation interface来创建JVM的。
- JNIEnv:JNI Interface Pointer, 是提供JNI Native函数的基础环境,线程相关,不同线程的JNIEnv相互独立。
JNIEnv只在当前线程中有效。本地方法不 能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对本地方法多次调用时,传递给该本地方法的JNIEnv是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv。
JavaVM则可以在进程中的各线程间共享。理论上一个进程可以有多个JavaVM,但Android只允许一个(JavaVm and JIEnv
)。需要强调的是JNIEnv是跟线程相关的。sdk文档中强调了do not cache JNIEnv*,要用的时候在不同线程中再通过JavaVM *jvm的方法来获取与当前线程相关的JNIEnv*。两者都可以理解为函数表(Function Pointer Table), 前者是使用Java程序创建的运行环境(从属于一个JVM)提供JNI Native函数。
- Java和Android中JavaVM对象有区别
在
java
里,每一个
process
可以产生多个
java vm
对象,但是在
android
上,每一个
process
只有一个
Dalvik
虚拟机对象,也就是在
android
进程中是通过有且只有一个虚拟器对象来服务所有
java
和
c/c++
代码。
Java
的
dex
字节码和
c/c++
的
*.so
同时运行
Dalvik
虚拟机之内,共同使用一个进程空间。之所以可以相互调用,也是因为有
Dalvik
虚拟机。
当
java
代码需要
c/c++
代码时,在
Dalvik
虚拟机加载进
*.so
库时,会先调用
JNI_Onload(),
此时就会把
JAVA VM
对象的指针存储于
c
层
jni
组件的全局环境中,在J
ava
层调用
C
层的本地函数时,调用
c
本地函数的线程必然通过
Dalvik
虚拟机来调用
c
层的本地函数,此时,
Dalvik
虚拟机会为本地的
C
组件实例化一个
JNIEnv
指针,该指针指向
Dalvik
虚拟机的具体的函数列表,当
JNI
的
c
组件调用J
ava
层的方法或者属性时,需要通过
JNIEnv
指针来进行调用。
当本地
c/c++
想获得当前线程所要使用的
JNIEnv
时,可以使用
Dalvik
虚拟机对象的
JavaVM
* jvm->GetEnv()
返回当前线程所在的
JNIEnv*。
附 JNIEnv和JavaVM的定义。他们位于头文件jni.h。$NDK\platforms\android-5\arch-arm\usr\include\jni.h的部分代码
- struct _JNIEnv;
- struct _JavaVM;
- #if defined(__cplusplus)
- typedef _JNIEnv JNIEnv; //C++使用这个类型
- typedef _JavaVM JavaVM; //C++使用这个类型
- #else
- typedef const struct JNINativeInterface* JNIEnv; //C使用这个类型
- typedef const struct JNIInvokeInterface* JavaVM; //C使用这个类型
- #endif
- struct JNINativeInterface
- {
- /****省略了的代码****/
- jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
- /****省略了的代码****/
- jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
- /****省略了的代码****/
- };
- struct _JNIEnv
- {
- const struct JNINativeInterface* functions;
- #if defined(__cplusplus)
- /****省略了的代码****/
- jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
- { return functions->GetMethodID(this, clazz, name, sig); }
- /****省略了的代码****/
- jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
- { return functions->GetStaticObjectField(this, clazz, fieldID); }
- /****省略了的代码****/
- #endif /*__cplusplus*/
- };
- struct JNIInvokeInterface
- {
- /****省略了的代码****/
- jint (*GetEnv)(JavaVM*, void**, jint);
- jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
- };
- struct _JavaVM
- {
- const struct JNIInvokeInterface* functions;
- #if defined(__cplusplus)
- /****省略了的代码****/
- jint GetEnv(void** env, jint version)
- { return functions->GetEnv(this, env, version); }
- jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
- { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
- #endif /*__cplusplus*/
- };