• 【Android JNI】JNIEnv和JavaVM的区别


     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组件的全局环境中,在Java层调用C层的本地函数时,调用c本地函数的线程必然通过Dalvik虚拟机来调用c层的本地函数,此时,Dalvik虚拟机会为本地的C组件实例化一个JNIEnv指针,该指针指向Dalvik虚拟机的具体的函数列表,当JNI的c组件调用Java层的方法或者属性时,需要通过JNIEnv指针来进行调用。  当本地c/c++想获得当前线程所要使用的JNIEnv时,可以使用Dalvik虚拟机对象的JavaVM* jvm->GetEnv()返回当前线程所在的JNIEnv*。
     
    建立全局的JavaVM的方法:
    1 //由java调用来建立JNI环境
    2 JNIEXPORT void Java_com_nan_thread_MyThreadActivity_setJNIEnv( JNIEnv* env, jobject obj)
    3 {
    4     //保存全局JVM以便在子线程中使用
    5     (*env)->GetJavaVM(env,&g_jvm);
    6     //不能直接赋值(g_obj = obj)
    7     g_obj = (*env)->NewGlobalRef(env,obj);
    8 }

     获得JNIEvn方法:

     1 JNIEnv *JNI_GetEnv(int *attach) {
     2     if (JNI_Jvm == NULL)
     3         return NULL;
     4 
     5     int status;
     6 
     7     //判断是否有AttachCurrentThread
     8     *attach = 0;
     9 
    10     JNIEnv *_jniEnv = NULL;
    11 
    12     status = (*JNI_Jvm)->GetEnv(JNI_Jvm, (void **) &_jniEnv, JNI_VERSION_1_6);
    13 
    14     if (status == JNI_EDETACHED || _jniEnv == NULL) {
    15         status = (*JNI_Jvm)->AttachCurrentThread(JNI_Jvm, &_jniEnv, NULL);
    16         if (status < 0) {
    17             _jniEnv = NULL;
    18         } else {
    19             *attach = 1;
    20         }
    21     }
    22     return _jniEnv;
    23 }

    回收env:

    1 int JNI_DelEnv() {
    2     if (JNI_Jvm == NULL)
    3         return -1;
    4 
    5     return (*JNI_Jvm)->DetachCurrentThread(JNI_Jvm);
    6 }

    在java线程中不能使用AttachCurrentThread、DetachCurrentThread方法来获取JNIEnv,否则会出错。

  • 相关阅读:
    从底层谈WebGIS 原理设计与实现(六):WebGIS中地图瓦片在Canvas上的拼接显示原理
    从底层谈WebGIS 原理设计与实现(五):WebGIS中通过行列号来换算出多种瓦片的URL 之在线地图
    从底层谈WebGIS 原理设计与实现(四):WebGIS中通过行列号来换算出多种瓦片的URL 之离线地图
    从底层谈WebGIS 原理设计与实现(三):WebGIS前端地图显示之根据地理范围换算出瓦片行列号的原理(核心)
    从底层谈WebGIS 原理设计与实现(二):探究本质,WebGIS前端地图显示之地图比例尺换算原理
    [leetcode]Rotate List
    [leetcode]Remove Element
    [leetcode]Binary Tree Level Order Traversal II
    [leetcode]Populating Next Right Pointers in Each Node II
    [leetcode]Construct Binary Tree from Inorder and Postorder Traversal
  • 原文地址:https://www.cnblogs.com/fnlingnzb-learner/p/7366025.html
Copyright © 2020-2023  润新知