【Android JNI】JNIEnv和JavaVM的区别

来源:互联网 发布:python字符串转字典 编辑:程序博客网 时间:2024/04/28 17:33
           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进程中是通过有且只有一个虚拟器对象来服务所有javac/c++代码。

           Java dex字节码和c/c++*.so同时运行Dalvik虚拟机之内,共同使用一个进程空间。之所以可以相互调用,也是因为有Dalvik虚拟机。java 代码需要c/c++代码时,在Dalvik虚拟机加载进*.so库时,会先调用JNI_Onload(),此时就会把JAVA VM对象的指针存储于cjni组件的全局环境中,在Java层调用C层的本地函数时,调用c本地函数的线程必然通过Dalvik虚拟机来调用c层的本地函数,此时,Dalvik虚拟机会为本地的C组件实例化一个JNIEnv指针,该指针指向Dalvik虚拟机的具体的函数列表,当JNIc组件调用Java层的方法或者属性时,需要通过JNIEnv指针来进行调用。  当本地c/c++想获得当前线程所要使用的JNIEnv时,可以使用Dalvik虚拟机对象的JavaVM* jvm->GetEnv()返回当前线程所在的JNIEnv*。

    
附       JNIEnv和JavaVM的定义。他们位于头文件jni.h。$NDK\platforms\android-5\arch-arm\usr\include\jni.h的部分代码
  1. struct _JNIEnv;  
  2.   
  3. struct _JavaVM;  
  4.   
  5. #if defined(__cplusplus)  
  6.   
  7. typedef _JNIEnv JNIEnv;                                 //C++使用这个类型  
  8.   
  9. typedef _JavaVM JavaVM;                                 //C++使用这个类型  
  10.   
  11. #else  
  12.   
  13. typedef const struct JNINativeInterface* JNIEnv;        //C使用这个类型  
  14.   
  15. typedef const struct JNIInvokeInterface* JavaVM;        //C使用这个类型  
  16.   
  17. #endif  
  18.   
  19. struct JNINativeInterface  
  20.   
  21. {  
  22.   
  23.     /****省略了的代码****/  
  24.   
  25.     jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);  
  26.   
  27.     /****省略了的代码****/  
  28.   
  29.     jobject     (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);  
  30.   
  31.     /****省略了的代码****/  
  32.   
  33. };  
  34.   
  35. struct _JNIEnv  
  36. {  
  37.     const struct JNINativeInterface* functions;  
  38.     #if defined(__cplusplus)  
  39.     /****省略了的代码****/  
  40.     jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)  
  41.     { return functions->GetMethodID(this, clazz, name, sig); }  
  42.     /****省略了的代码****/  
  43.     jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)  
  44.     { return functions->GetStaticObjectField(this, clazz, fieldID); }  
  45.     /****省略了的代码****/  
  46.     #endif /*__cplusplus*/  
  47. };  
  48.   
  49. struct JNIInvokeInterface  
  50. {  
  51.      /****省略了的代码****/  
  52.     jint (*GetEnv)(JavaVM*, void**, jint);  
  53.     jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);  
  54. };  
  55.   
  56. struct _JavaVM  
  57. {  
  58.     const struct JNIInvokeInterface* functions;  
  59.     #if defined(__cplusplus)  
  60.     /****省略了的代码****/  
  61.     jint GetEnv(void** env, jint version)  
  62.     { return functions->GetEnv(this, env, version); }  
  63.     jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)  
  64.     { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }  
  65.     #endif /*__cplusplus*/  
  66. }; 
0 0