JNI完全指南(十)——JavaVM与JNIEnv

JNI完全指南版本:1

作者:陈小默

声明:禁止商业,禁止转载

十、JavaVM与JNIEnv

10.1 JNIEnv

JNIEnv类型是一个指向全部JNI方法的指针。该指针只在创建它的线程有效,不能跨线程传递。其声明如下:

struct_JNIEnv;

struct_JavaVM;

typedefconststructJNINativeInterface*C_JNIEnv;

#if defined(__cplusplus)

typedef_JNIEnvJNIEnv;

typedef_JavaVMJavaVM;

#else

typedefconststructJNINativeInterface*JNIEnv;

typedefconststructJNIInvokeInterface*JavaVM;

#endif

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

structJNINativeInterface{

...

jint(*GetVersion)(JNIEnv*);

...

};

于是我们在使用此方法的方式为:

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

对于C++而言,这里进行了封装,其声明为

struct_JNIEnv{

conststructJNINativeInterface*functions;

#if defined(__cplusplus)

jintGetVersion()

{returnfunctions->GetVersion(this);}

...

#endif/*__cplusplus*/

};

这里可以看出,在C++环境的情况下,这里的使用方式为:

jint version=env->GetVersion();

剩余的方法关于C和C++实现的差别基本如此。我们可以通过以下方法获取当前的JNI版本:

jintGetVersion(JNIEnv*env);

这里的返回值是宏定义的常量,我们可以使用获取到的值与下列宏进行匹配来知道当前的版本:

#defineJNI_VERSION_1_10x00010001

#defineJNI_VERSION_1_20x00010002

#defineJNI_VERSION_1_40x00010004

#defineJNI_VERSION_1_60x00010006

10.2 JavaVM

JavaVM是虚拟机在JNI中的表示,一个JVM中只有一个JavaVM对象,这个对象是线程共享的。通过JNIEnv我们可以获取一个Java虚拟机对象,其函数如下:

jint GetJavaVM(JNIEnv *env, JavaVM **vm);

vm:用来存放获得的虚拟机的指针的指针。

return:成功返回0,失败返回其他。

我们来看一下在C语言环境下,JNI中JVM的声明:

/*

* JNI invocation interface.

*/

structJNIInvokeInterface{

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*);

};

JNI中操作JVM的声明如下:

jint JNI_GetDefaultJavaVMInitArgs(void*);

jint JNI_CreateJavaVM(JavaVM**,JNIEnv**,void*);

jint JNI_GetCreatedJavaVMs(JavaVM**,jsize,jsize*);

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM*vm,void*reserved);

JNIEXPORTvoidJNICALL JNI_OnUnload(JavaVM*vm,void*reserved);

10.2.1 创建JVM一般而言,调用JNI_CreateJavaVM创建JVM的线程被称为主线程。理论上来说,此方法不允许用户调用。

jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);

p_vm:保存创建的虚拟机的指针。

p_env:保存获得到的JNIEnv对象的指针。

vm_args:一个JavaVMInitArgs类型的指针,用来设置初始化参数。

return:创建成功返回JNI_OK,失败返回其他。

其中JavaVMInitArgs是存放虚拟机参数的结构体,定义如下:

typedefstructJavaVMOption{

constchar*optionString;

void*extraInfo;

}JavaVMOption;

typedefstructJavaVMInitArgs{

jint version;

jint nOptions;

JavaVMOption*options;

jboolean ignoreUnrecognized;

}JavaVMInitArgs;

以下举例说明创建过程:

JavaVMInitArgsvm_args;

JavaVMOptionoptions[4];

options[0].optionString="-Djava.compiler=NONE";/* disable JIT */

options[1].optionString="-Djava.class.path=c:\myclasses";/* user classes */

options[2].optionString="-Djava.library.path=c:\mylibs";/* set native library path */

options[3].optionString="-verbose:jni";/* print JNI-related messages */

vm_args.version=JNI_VERSION_1_2;

vm_args.options=options;

vm_args.nOptions=4;

vm_args.ignoreUnrecognized=TRUE;

/* Note that in the JDK/JRE, there is no longer any need to call

* JNI_GetDefaultJavaVMInitArgs.

*/

res=JNI_CreateJavaVM(&vm,(void**)&env,&vm_args);

if(res<0)...

10.2.2 链接到虚拟机

JNIEnv指针仅在创建它的线程有效。如果我们需要在其他线程访问JVM,那么必须先调用AttachCurrentThread将当前线程与JVM进行关联,然后才能获得JNIEnv对象。当然,我们在必要时需要调用DetachCurrentThread来解除链接。jint AttachCurrentThread(JavaVM* vm , JNIEnv** env , JavaVMAttachArgs* args);

vm:虚拟机对象指针。

env:用来保存得到的JNIEnv的指针。

args:链接参数,参数结构体如下所示。

return:链接成功返回0,连接失败返回其他。

structJavaVMAttachArgs{

jint version;/* must be >= JNI_VERSION_1_2 */

constchar*name;/* NULL or name of thread as modified UTF-8 str */

jobjectgroup;/* global ref of a ThreadGroup object, or NULL */

};

10.2.3 解除与虚拟机的连接下列函数用来解除当前线程与虚拟机之间的链接:

jint DetachCurrentThread(JavaVM* vm);

10.2.4 卸载虚拟机

调用JNI_DestroyJavaVM函数将会卸载当前使用的虚拟机。jint DestroyJavaVM(JavaVM* vm);

10.2.5 动态加载本地方法

在JNI中有一组特殊的函数:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM*vm,void*reserved);

JNIEXPORTvoidJNICALL JNI_OnUnload(JavaVM*vm,void*reserved);

这一组函数的作用就是负责Java方法和本地C函数的链接。例如,我在Java代码中声明了这样一段本地代码:

packagecom.github.cccxm;

classNativeLib{

publicstaticnativeStringgetName(intnumber);

}

一般情况下,我们需要在本地源文件中声明如下:

JNIEXPORT jstring JNICALLJava_com_github_cccxm_NativeLib_getName(JNIEnv*env,jobject thiz,jint number);

那么,Java方法和本地函数之间的映射关系编译器已经帮我们做了。如果在某些场景下,我们需要动态地加载本地方法。例如,我们现在仍使用NativeLib类,但是在本地代码中,我们声明了一个没有按照JNI规范命名的本地函数:

JNIEXPORT jstring JNICALL getName(JNIEnv*env,jclass clazz);

那么我们就必须使用动态关联的方式实现Java方法与本地函数的映射,代码如下:

extern"C"

JNIEXPORT jstring JNICALL getName(JNIEnv*env,jobject thiz,intnumber){

ALOGE("number is %d",number);

returnenv->NewStringUTF("hello world");

}

staticconstchar*CLASS_NAME="com/github/cccxm/NativeLib";//类名

staticJNINativeMethodmethod={//本地方法描述

"getName",//Java方法名

"(I)Ljava/lang/String;",//方法签名

(void*)getName//绑定本地函数

};

staticbool

bindNative(JNIEnv*env){

jclass clazz;

clazz=env->FindClass(CLASS_NAME);

if(clazz==NULL){

returnfalse;

}

returnenv->RegisterNatives(clazz,&method,1)==0;

}

JNIEXPORT jint JNICALL

JNI_OnLoad(JavaVM*vm,void*reserved){

JNIEnv*env=NULL;

jint result=-1;

if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){

returnresult;

}

boolres=bindNative(env);

ALOGE("bind result is %s",res?"ok":"error");

// 返回jni的版本

returnJNI_VERSION_1_6;

}注意:JNI_OnLoad方法在每一个.so库中只能存在一个。

10.2.6 卸载本地方法

在上面的例子中我们了解了如何动态加载一个本地方法,那么有加载就有卸载,接下来,我们看一下如何卸载一个本地方法。JNI_OnLoad方法是在动态库被加载时调用,而JNI_OnUnload则是在本地库被卸载时调用。所以这两个函数就是一个本地库最重要的两个生命周期方法。如果没有显式的卸载一个本地库则不会看到此方法被调用。以下举例说明用法:

...

staticbool

unBindNative(JNIEnv*env){

jclass clazz;

clazz=env->FindClass(CLASS_NAME);

if(clazz==NULL){

returnfalse;

}

returnenv->UnregisterNatives(clazz)==0;

}

JNIEXPORTvoidJNICALL

JNI_OnUnload(JavaVM*vm,void*reserved){

JNIEnv*env=NULL;

jint result=-1;

if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){

return;

}

boolres=unBindNative(env);

ALOGE("unbind result is %s",res?"ok":"error");

}

10.2.7 获取默认虚拟机初始化参数通过以下函数能够获取到默认的虚拟机初始化参数:

jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);

vm_args:JavaVMInitArgs类型的参数,该结构体声明在10.2.1

return:获取成功返回JNI_OK,失败返回其他。

10.2.8 获取Java虚拟机通过以下方法可以获取到已经被创建的Java虚拟机对象。

jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);

vmBuf:用来保存Java虚拟机的缓冲区

bufLen:缓冲区长度。

nVms:实际获得到的Java虚拟机个数。

return:获取成功返回JNI_OK,失败返回其他。

后记

虽然整个系列内容并不多,但是中途任然好几次的想放弃,看来写系列博客并不是一件什么轻松的事。

这里送上一首颜真卿的诗,与君共勉三更灯火五更鸡,

正是男儿读书时。

黑发不知勤学早,

白首方悔读书迟。

本系列完结,感谢各位读者的大力支持。其中内容会不断补充。如有不当之处,恳请批评指教。

java.vm.info_JNI完全指南(十)——JavaVM与JNIEnv相关推荐

  1. java vm art_Android虚拟机art流程:JavaVM 和 JNIEnv 的初始化 - 神农笔记

    JNIEnv* env; JavaVM* mJavaVM: if (startVm(&mJavaVM, &env, zygote) != 0) { return; } 首先我们得要先看 ...

  2. Java多线程编程实战指南+设计模式篇pdf

    下载地址:网盘下载 随着CPU 多核时代的到来,多线程编程在充分利用计算资源.提高软件服务质量方面扮演了越来越重要的角色.而 解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案.然 ...

  3. java性能优化权威指南_Java性能优化权威指南 PDF扫描[132MB]

    Java性能优化权威指南主要为Java SE 和Java EE 应用的性能调优提供建议.主要包括以下几方面:性能监控.性能分析.Java HotSpot VM 调优.高效的基准测试以及Java EE ...

  4. Creating a Java VM from Android Native Code

    If you're writing native / JNI code for Android, it's probably as native method of an Android app. T ...

  5. Kotlin实战指南十九:use 函数魔法

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/117366756 本文出自[赵彦军的博客] 文章目录 往期精彩文章 use函数 往期 ...

  6. Kotlin实战指南十八:open、internal 关键字使用

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/117365712 本文出自[赵彦军的博客] 文章目录 往期精彩文章 open关键字 ...

  7. Java 本地接口(JNI)编程指南和规范学习笔记2

    1.JNI和线程: Java 虚拟器支持控制并发的在一样地址空间中执行的多线程,多线程可以访问同一个对象,同一个文件描述符. 多线程的限制: 一个"JNIEnv"指针只在和其关联的 ...

  8. Java性能优化权威指南--笔记

    出处:http://xiongpq.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 原文 ...

  9. 【校招分享】Java非科班自学指南

    作者:一星如月看多时 链接:[校招干货]Java非科班自学指南V1.0_招聘信息_牛客网 来源:牛客网 本文针对应届校招生,面向大厂面试学习,讲究打牢基础的同时追求速成,适合还有一年以上时间参加校招的 ...

最新文章

  1. 如何查看用index.php,为什么整个网站使用一个index.php页面?
  2. 网络计算机的广域性有什么,为什么计算机网络有局域网?
  3. mysql 做回归模型_GitHub - themycode/intelligent-test-platform: intelligent-test-platform
  4. php发送get、post请求的几种方法
  5. 简单好用的 Linux/Windows 面板
  6. Git初学札记(一)————Git简介与安装
  7. matlab旋转机械转子故障信号仿真,旋转机械转子不对中故障诊断技术研究解说.docx...
  8. postman-常见问题解决方案记录
  9. VS中出现 模块计算机类型“x86”与目标计算机类型“x64”冲突
  10. postfix所谓的监控功能只是利用sender_bcc而已
  11. [poj1410]Intersection
  12. Simulink中.sxl文件与.mdl文件的区别
  13. Ubuntu20.04安装输入法
  14. 牛顿柯特斯求积公式matlab,牛顿-柯特斯求积公式总结.ppt
  15. 生活计算机的模拟环境中,惊人发现:人类生活在高等文明创建的模拟环境中!...
  16. 关于Intriguing properties of neural networks的理解
  17. 计算机软考网络工程师 查询,软考网络工程师怎么查询报名是否成功?
  18. 如何将树莓派网关连接到TTN——手把手教你如何将树莓派网关连接到服务器之第四篇
  19. POJ1190 生日蛋糕(回溯法)
  20. 微信可以一键恢复好友吗?

热门文章

  1. 安卓开发问题之 Unable to instantiate application com.android.tools.fd.runtime.BootstrapApplication
  2. Intent Service 和Service的区别
  3. C语言重点——指针篇(一文让你完全搞懂指针)| 从内存理解指针 | 指针完全解析
  4. matlab求解erfc方程
  5. 分享:Java 开发精美艺术二维码
  6. 手机空间不足?教你2招省5G空间
  7. Git的基本使用方法教程(入门级)
  8. 2013 12 android 凯立德秋季高清旗舰 百度云,【荐】2013.4.12凯立德春季完美安卓手机版+4月23日更新车用高清版...
  9. qnx的汽车全液晶仪表-基于qnx系统的汽车仪表-车机系统开发
  10. 初看一脸问号,看懂直接跪下