JNI/NDK入门指南之JavaVM和JNIEnv

Android JNI/NDK入门指南目录

JNI/NDK入门指南之正确姿势了解JNI和NDK
JNI/NDK入门指南之JavaVM和JNIEnv
JNI/NDK入门指南之JNI数据类型,描述符详解
JNI/NDK入门指南之jobject和jclass
JNI/NDK入门指南之javah和javap的使用和集成
JNI/NDK入门指南之Eclipse集成NDK开发环境并使用
JNI/NDK入门指南之JNI动/静态注册全分析
JNI/NDK入门指南之JNI字符串处理
JNI/NDK入门指南之JNI访问数组
JNI/NDK入门指南之C/C++通过JNI访问Java实例属性和类静态属性
JNI/NDK入门指南之C/C++通过JNI访问Java实例方法和类静态方法
JNI/NDK入门指南之JNI异常处理
JNI/NDK入门指南之JNI多线程回调Java方法
JNI/NDK入门指南之正确姿势了解,使用,管理,缓存JNI引用
JNI/NDK入门指南之调用Java构造方法和父类实例方法
JNI/NDK入门指南之C/C++结构体和Java对象转换方式一
JNI/NDK入门指南之C/C++结构体和Java对象转换方式二


引言

  在前面的章节JNI数据类型,描述符详解中,我们详解了JNI数据类型和描述符的一些概念,那么在今天我们将要熟悉掌握JNI的开发中另外两个关键知识点JavaVMJniEnv


一.细说JavaVM

JavaVM,英文全称是Java virtual machine,用咋中国话来说就是Java虚拟机。一个JVM中只有一个JavaVM对象,这个JavaVM则可以在进程中的各线程间共享的,这个特性在JNI开发中是非常重要的。

1.获取JavaVM虚拟机接口

在JNI的开发中有两种方法可以获取JavaVM,下面来分别介绍一下。
方式一:
在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数)。第一个参数会传入JavaVM指针。代码如下:

jint JNI_OnLoad(JavaVM * vm, void * reserved){JNIEnv * env = NULL;jint result = -1;if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {LOGE(TAG, "ERROR: GetEnv failed\n");goto bail;}result = JNI_VERSION_1_4;bail:return result;

方式二:
在Native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)可以得到JavaVM指针。我们的Android系统是利用第二种方式来创建art虚拟机的的,具体的创建过程我就不想说了,这个不是本文讲解的重点。对于以上两种获取JavaVM的方式,都可以用全局变量,比如JavaVM* g_jvm来保存获得的指针以便在任意上下文中使用。

方式三:
通过JNIEnv获取JavaVM,具体参考代码如下:

JNIEXPORT void JNICALL Java_com_xxx_android2native_JniManager_openJni(JNIEnv * env, jobject object)
{LOGE(TAG, "Java_com_xxx_android2native_JniManager_openJni");//注意,直接通过定义全局的JNIEnv和Object变量,在此保存env和object的值是不可以在线程中使用的//线程不允许共用env环境变量,但是JavaVM指针是整个jvm共用的,所以可以通过下面//的方法保存JavaVM指针,在线程中使用env->GetJavaVM(&gJavaVM);//同理,jobject变量也不允许在线程中共用,因此需要创建全局的jobject对象在线程//中访问该对象gJavaObj = env->NewGlobalRef(object);gIsThreadStop = 0;

2.查看JavaVM定义

通过前面的章节我们对JavaVM有了一定的了解,下面让我们看看JNI中对JavaVM的申明,JavaVM申明在jni.h文件里面,这个你一定不会陌生,因为我们在JNI开发中,必定要引入#include <jni.h>头文件。
C语言中JavaVM声明如下

struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;//C语言定义
#endif/** JNI invocation interface.*/
struct JNIInvokeInterface {void*       reserved0;void*       reserved1;void*       reserved2;jint        (*DestroyJavaVM)(JavaVM*);jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);jint        (*DetachCurrentThread)(JavaVM*);jint        (*GetEnv)(JavaVM*, void**, jint);jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
};

C++中JavaVM声明如下

struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif/** C++ version.*/
struct _JavaVM {const struct JNIInvokeInterface* functions;#if defined(__cplusplus)jint DestroyJavaVM(){ return functions->DestroyJavaVM(this); }jint AttachCurrentThread(JNIEnv** p_env, void* thr_args){ return functions->AttachCurrentThread(this, p_env, thr_args); }jint DetachCurrentThread(){ return functions->DetachCurrentThread(this); }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*/
};

二.细说JNIEnv

JNIEnv,英文全称是Java Native Interface Environment,用咋中国话来说就是Java本地接口环境。在进行JNI编程开发的时候,使用javah生成Native方法对应的Native函数声明,会发现所有的Native函数的第一个参数永远是JNIEnv指针,如下所示:

/** Class:     com_xxx_object2struct_JniTransfer* Method:    getJavaBeanFromNative* Signature: ()Lcom/xxx/object2struct/JavaBean;*/
JNIEXPORT jobject JNICALL Java_com_xxx_object2struct_JniTransfer_getJavaBeanFromNative(JNIEnv *, jclass);/** Class:     com_xxx_object2struct_JniTransfer* Method:    transferJavaBeanToNative* Signature: (Lcom/xxx/object2struct/JavaBean;)V*/
JNIEXPORT void JNICALL Java_com_xxx_object2struct_JniTransfer_transferJavaBeanToNative(JNIEnv *, jclass, jobject);

JNIEnv是提供JNI Native函数的基础环境,线程相关,不同线程的JNIEnv相互独立,并且JNIEnv是一个JNI接口指针,指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地方法通过JNI函数来访问JVM中的数据结构,详情如下图:

通过上面的图示,我们应该更加了解JNIEnv只在当前线程中有效。本地方法不 能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对本地方法多次调用时,传递给该本地方法的JNIEnv是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv。

1.查看JNIEnv定义

通过前面的章节我们对JavaVM有了一定的了解,下面让我们看看JNI中对JNIEnv的申明,可以看出JNIEnv是一个包含诸多JNI函数的结构体,JNIEnv申明在jni.h文件里面,这个你一定不会陌生,因为我们在JNI开发中,必定要引入#include <jni.h>头文件。

C语言中JNIEnv声明如下

struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;//C的定义
typedef const struct JNIInvokeInterface* JavaVM;
#endif/** Table of interface function pointers.*/
struct JNINativeInterface {void*       reserved0;void*       reserved1;void*       reserved2;void*       reserved3;jint        (*GetVersion)(JNIEnv *);...
}

在C语言中对JNIEnv下GetVersion()方法使用如下:

  jint version = (*env)->GetVersion(env);

C++中JNIEnv声明如下

struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;//C的定义
typedef const struct JNIInvokeInterface* JavaVM;
#endif/** C++ object wrapper.** This is usually overlaid on a C struct whose first element is a* JNINativeInterface*.  We rely somewhat on compiler behavior.*/
struct _JNIEnv {/* do not rename this; it does not seem to be entirely opaque */const struct JNINativeInterface* functions;#if defined(__cplusplus)jint GetVersion(){ return functions->GetVersion(this); }...
}

在C++中对JNIEnv下GetVersion()方法使用如下:

   jint version = env->GetVersion();

仅从这一部分我们可以看出的是对于JNIEnv在C语言环境和C++语言环境中的实现是不一样的。也就是说我们在C语言和C++语言中对于JNI方法的调用是有区别的。这里我们以GetVersion函数为例说明,其在C和C++中的不同。

2.JNIEnv结构体中JNI函数划分

通过分析前面章节C/C++中JNIEnv结构体的话,不难发现,这个结构体当中包含了几乎有所的JNI函数,大致可以分为如下几类:

函数名 功能
FindClass 该函数用于加载本地定义的类
GetObjectClass 通过对象获取这个类
NewGlobalRef 创建 obj 参数所引用对象的新全局引用
NewObject 构造新 Java 对象
NewString 利用 Unicode 字符数组构造新的 java.lang.String 对象
New<Type>Array 创建类型为Type的数组对象
Get<Type>Field 获取类型为Type的字段
Set<Type>Field 设置类型为Type的字段的值
GetStatic<Type>Field 获取类型为Type的static的字段
SetStatic<Type>Field 设置类型为Type的static的字段的值
Call<Type>Method 调用返回类型为Type的方法
CallStatic<Type>Method 调用返回值类型为Type的static方法

常见的JNI函数还有一些,这里由于篇幅问题就不过多介绍了。这里推荐一个博客JNI学习积累之一 ---- 常用函数大全里面有比较详细的描述了,大家可以仔细阅读。当然最好的办法,就是实际使用中慢慢品尝了。

3.获取JNIEnv

如果是在同一个线程中需要使用JNIEnv,这个通过前面的讲解我想读者朋友们一定会脱口而出,使用参数传递,是的这个是可以做到的。但是使用跨线程呢?这个一般会使用到全局引用了,参加如下代码,具体可以参见我的博客Android和C/C++通过Jni实现通信方式一中对于跨线程使用JNIEnv有比较详细的介绍了。

static void* native_thread_exec(void *arg)
{LOGE(TAG,"nativeThreadExec");LOGE(TAG,"The pthread id : %d\n", pthread_self());JNIEnv *env;//从全局的JavaVM中获取到环境变量gJavaVM->AttachCurrentThread(&env,NULL);//get Java class by classPath//获取Java层对应的类jclass thiz = env->GetObjectClass(gJavaObj);//get Java method from thiz//获取Java层被回调的函数jmethodID nativeCallback = env->GetMethodID(thiz,"callByJni","(I)V");int count = 0;//线程循环while(!gIsThreadStop){sleep(2);//跨线程回调Java层函数env->CallVoidMethod(gJavaObj,nativeCallback,count++);}gJavaVM->DetachCurrentThread();LOGE(TAG,"thread stoped");return ((void *)0);
}
JNIEXPORT void JNICALL Java_com_xxx_android2native_JniManager_openJni(JNIEnv * env, jobject object)
{LOGE(TAG, "Java_com_xxx_android2native_JniManager_openJni");//注意,直接通过定义全局的JNIEnv和Object变量,在此保存env和object的值是不可以在线程中使用的//线程不允许共用env环境变量,但是JavaVM指针是整个jvm共用的,所以可以通过下面//的方法保存JavaVM指针,在线程中使用env->GetJavaVM(&gJavaVM);//同理,jobject变量也不允许在线程中共用,因此需要创建全局的jobject对象在线程//中访问该对象gJavaObj = env->NewGlobalRef(object);gIsThreadStop = 0;
}

三.Java和Android中JavaVM对象有区别

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

参考博客:
https://blog.csdn.net/CV_Jason/article/details/80026265
https://www.cnblogs.com/fnlingnzb-learner/p/7366025.html

JNI/NDK入门指南之JavaVM和JNIEnv相关推荐

  1. JNI/NDK入门指南之jobject和jclass

          JNI/NDK入门指南之jobject和jclass Android JNI/NDK入门指南目录 JNI/NDK入门指南之正确姿势了解JNI和NDK JNI/NDK入门指南之JavaVM和 ...

  2. JNI/NDK开发指南(八)——调用构造方法和父类实例方法

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/44002089 在第6章我们学习到了在Native层如何调用Java静态方法和实例方法,其中 ...

  3. JNI NDK入门详解

    Android开发中,由于各种原因(跨平台,高性能,敏感数据处理等),这时候需要用到大家耳熟能详的JNI(Java Native Interface).本篇文章将带大家复习一下JNI中那些常用的知识点 ...

  4. java.vm.info_JNI完全指南(十)——JavaVM与JNIEnv

    JNI完全指南(十)--JavaVM与JNIEnv JNI完全指南版本:1 作者:陈小默 声明:禁止商业,禁止转载 十.JavaVM与JNIEnv 10.1 JNIEnv JNIEnv类型是一个指向全 ...

  5. JNI/NDK开发指南(二)——JVM查找java native方法的规则

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/41854185 通过第一篇文章,大家明白了调用native方法之前,首先要调用System. ...

  6. JNI/NDK开发指南(三)——JNI数据类型及与Java数据类型的映射关系

    2019独角兽企业重金招聘Python工程师标准>>> 转载请注明出处:http://blog.csdn.net/xyang81/article/details/42047899 当 ...

  7. android -------- NDK 入门指南

    NDK介绍 原生开发工具包 (NDK) 是一组可让您在 Android 应用中利用 C 和 C++ 代码的工具. 可用以从您自己的源代码构建,或者利用现有的预构建库. NDK 不适用于大多数初学的 A ...

  8. JNI/NDK开发指南(十一)——JNI异常处理

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/45770551 异常简介 异常,显而意见就是程序在运行期间没有按照正常的程序逻辑执行,在执行 ...

  9. NDK JNI JavaVM、JNIEnv和jobject的理解

    JavaVM.JNIEnv和jobject的理解 三者特点: 1)JavaVM:能够跨越线程,能够跨越函数: 2)JNIEnv:不能跨越线程,否则奔溃,可以跨越函数: 3)jobject:不能跨越线程 ...

  10. 【Android FFMPEG 开发】C++ 回调 Java 方法 模板 ( JavaVM *vm | JNIEnv *env | jobject instance | 引用类型 | 模板代码示例 )

    文章目录 I . Native 调用 Java 方法 II . JNIEnv *env 与 jobject instance III . JavaVM *vm IV . 局部引用 与 全局引用 分析 ...

最新文章

  1. java安装_快速提示:Java中的ISO 8601持续时间
  2. python中easygui最新下载教程_python怎么下载easygui
  3. JavaScript:document.execCommand()的用法
  4. ubuntu下安装Qt的过程以及遇到的问题和解决方案
  5. oracle中使用sys_connect_by_path进行表中行值连接
  6. centos7 docker删除端口映射_容器Docker详解
  7. stm32F051系列 单片机引脚定时器输出pwm波形控制风扇转速
  8. 50.本地VMware环境虚拟机的异地(Azure)容灾(上)
  9. 如何用iMazing Profile Editor编辑配置文件
  10. Servlet文件下载
  11. 看了一个大牛的博客,发现了一个很好的文章-初学PHP进
  12. hashcat的使用方法
  13. Java反射创建对象效率高还是通过new创建对象的效率高?
  14. 服务器返回消息为空iOS
  15. 身份证复印件的正确使用方法- -
  16. MSSQL Server 2008 - express 版 安装 企业管理器Management Studio
  17. 来世你还能和你的父母重逢吗?
  18. C# 阿里云短信接口调用(不使用SDK,单文件完成)
  19. Meta R-CNN : Towards General Solver for Instance-level Low-shot Learning 论文笔记
  20. 嵌入式开发需掌握的技能有哪些

热门文章

  1. cxf框架Demo1
  2. 迈高图手机版_迈高图(地图数据器)|迈高图(地图数据器) v2.11.8.0官方版 - 系统天堂...
  3. 四维图新地图坐标_移动端地图技术分享
  4. 超逼真AI设计师,谷歌大脑推出新的文本生成图像模型Imagen
  5. 下载安装php详细教程(在安装配置apache之后)
  6. 单片机c语言轻松入门 pdf,单片机系统C语言轻松入门.pdf
  7. Clover 驱动文件夹_声卡驱动的另一个办法(ppleHDA Patcher软件说明)
  8. php常用字体大小,推荐:PHP编辑器常用的几种字体下载
  9. 【标准】ISO14001:2015新版标准正式发布
  10. 2021年中国新型电力系统行业市场现状、竞争格局与发展方向分析「图」