当前位置: 首页 > news >正文

JNI编程如何巧妙获取JNIEnv

这里穿插一篇Java JNI相关的知识点,总结一下自己平时工作心得,相信会对做JNI编程的同学有所帮助。

背景

作者目前在做Android项目,但大多数逻辑都会在Native层实现,不可避免的需要在Native层使用C++去调用Java的方法,但是在Native层调用Java方法就需要JNIEnv指针,那如何方便的获取JNIEnv的指针呢?

分析

如下代码:

JNIEXPORT void Java_com_Activity_testEnv( JNIEnv* env, jobject obj) {
   g_obj = env->NewGlobalRef(obj);
}

我们平时可能都见过这种代码,Java层定义了Native的testEnv方法,在Native层就有一个相应的方法与之对应,同时带有JNIEnv*和jobject的参数(在static的native方法中会是jclass类型的参数),但是如果这种代码呢?

JNIEXPORT void Java_com_Activity_testEnv(JNIEnv* env, jobject obj) {
    g_obj = env->NewGlobalRef(obj);
    func1(env);
    func2(env);
    func3(env);
    func4(env);
    func5(env);
    func6(env);
    func7(env);
    func8(env);
    func9(env);
}

定义的每个函数都需要将JNIEnv*作为参数传递,如果函数内还有很多嵌套,这种方式简直就是灾难,都需要将JNIEnv *作为参数传递?是不是很麻烦?你可能有这样的想法,我们把env存到本地不就可以了吗,答案是不可以,因为每一个Java线程都会有一个对应的env,我们在Native层无法感知到是哪一个Java线程,保存的env可能当时有效,换一个线程就会失效,而且Native层的函数也可以是从Native线程(即pthread创建的线程)调用,与Java线程没有关联,保存的env必然是失效的,那怎么办呢?

解决:使用JavaVM,这里先介绍下JNIEnv和JavaVM的概念。

JavaVM:Java虚拟机在Native层的代表,在Android中一个进程只有一个JavaVM,所有的线程共用一个JavaVM。

JNIEnv:Java调用Native语言的环境,是一个封装了几乎所有JNI方法的指针,每一个Java线程都有一个对应的JNIEnv,JNIEnv只在当前线程可用,不能跨线程使用,不同线程的JNIEnv彼此独立。在Native环境中创建的线程,如果需要调用JNI方法,必须要调用AttachCurrentThread()与JVM进行关联,使用后也需要调用DetachCurrentThread()来解除关联。

小总结:

在Android进程中,在Native层,通过任何一个可用的JNIEnv都可以获取到整个进程唯一的JavaVM,在任何线程中都可以通过JavaVM获取当前线程可用的JNIEnv,如果是Native线程还需要额外与JVM进行关联。

到这里大家可能都清楚了,只要能够得到JavaVM就可以解决JNIEnv的问题,那如何获取JavaVM呢?

如何获取JavaVM?

这里只介绍Android中常见的获取JavaVM的方法。

方法一:在Android中调用Native方法前通常都会先加载Native的动态链接库,通常都是使用这种方法:

System.loadLibrary(xxx);

这个方法调用后Native层会自动调用JNI_OnLoad方法:

JavaVM *global_jvm;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    global_jvm = vm;
}

这样JavaVM就已经获取到啦,将其保存起来即可。

方法二:通过JNIEnv获取JavaVM,在程序的最开始写一个类似于初始化功能的函数,传到Native层一个可用的JNIEnv,之后就可以获取到JavaVM。

JavaVM *global_jvm;
void get_jvm(JNIEnv *env) {
   env->GetJavaVM(&global_jvm);
}

如何通过JavaVM获取JNIEnv?

直接看代码:

JNIEnv *get_env(int *attach) {
   if (global_jvm == NULL) return NULL;


   *attach = 0;
   JNIEnv *jni_env = NULL;


   int status = global_jvm->GetEnv((void **)&jni_env, JNI_VERSION_1_6);


   if (status == JNI_EDETACHED || jni_env == NULL) {
       status = global_jvm->AttachCurrentThread(&jni_env, NULL);
       if (status < 0) {
           jni_env = NULL;
       } else {
           *attach = 1;
       }
  }
   return jni_env;
}
void del_env() {
   return global_jvm->DetachCurrentThread();
}

通过前面保存的JavaVM就可以获取到JNIEnv,注意get_env函数有一个参数attach,attach是一个出参,这个参数返回1时,代表当前线程是Native线程,使用完后需要调用del_env()断开与JVM的链接。

使用方法如下:

jobject new_global_object(jobject obj) {
   int attach = 0;
   JNIEnv *env = get_env(&attach);
   jobject ret = env->NewGlobalRef(obj);
   if (attach == 1) {
       del_env();
   }
   return ret;
}

使用这种方式后,我们再也不用被如何获取JNIEnv的问题困扰啦。

参考资料

https://blog.csdn.net/afei__/article/details/80986203

https://www.cnblogs.com/fnlingnzb-learner/p/7366025.html

https://www.cnblogs.com/MMLoveMeMM/archive/2014/07/15/3846448.html


技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

喜欢就点个「在看」吧 ▽

相关文章:

  • 最新 Android 面试点梳理,我收藏了你呢?
  • 三年Android开发,跳槽腾讯音乐,历经三面终获Offer,定级T2-1(超全面试题+学习经验总结)...
  • 「Android音视频编码那点破事」序章
  • Android短文:理解插值器和估值器
  • 用户调研:音视频方面的书籍,哪些内容才是你需要的?
  • 职场PUA到底有多可怕?
  • Fragment 的过去、现在和将来
  • 数字图像处理领域中常见的几种色彩模式
  • Android VSYNC (Choreographer)与UI刷新原理分析
  • Android 实现 图片 转 字符画 效果
  • Android 实现 视频 转 字符画效果
  • 在 iOS 上用 Shader 实现 图片 转 字符画 效果~~
  • 【每周一记-003】~~~
  • Android Canvas 绘制小黄人
  • OpenGL ES 之 LUT(滤镜基准图)
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 【Linux系统编程】快速查找errno错误码信息
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • 03Go 类型总结
  • 2017-08-04 前端日报
  • C# 免费离线人脸识别 2.0 Demo
  • Consul Config 使用Git做版本控制的实现
  • Elasticsearch 参考指南(升级前重新索引)
  • ES6语法详解(一)
  • GitUp, 你不可错过的秀外慧中的git工具
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • Mac转Windows的拯救指南
  • Python打包系统简单入门
  • Redis字符串类型内部编码剖析
  • Shadow DOM 内部构造及如何构建独立组件
  • 从输入URL到页面加载发生了什么
  • 给第三方使用接口的 URL 签名实现
  • 给新手的新浪微博 SDK 集成教程【一】
  • 关于extract.autodesk.io的一些说明
  • 记一次用 NodeJs 实现模拟登录的思路
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 收藏好这篇,别再只说“数据劫持”了
  • 原生js练习题---第五课
  • MPAndroidChart 教程:Y轴 YAxis
  • 如何在招聘中考核.NET架构师
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • ![CDATA[ ]] 是什么东东
  • #git 撤消对文件的更改
  • #LLM入门|Prompt#3.3_存储_Memory
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (11)MSP430F5529 定时器B
  • (12)目标检测_SSD基于pytorch搭建代码
  • (HAL库版)freeRTOS移植STMF103
  • (LeetCode 49)Anagrams
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**