重新认识JNIEnv

引言

抛开一堆概念,我们从本质出发。

java 调用c++

我们开发移动端或者后端服务 ,都是用的java 或者kotlin 语言。有时候我们需要用c++的一些库(ocr识别/导航的算法/ 等)

因为一些跟硬件相关的接口或者系统的api 都是c++写的。

C++调用Java

1、 回调/事件通知, 比如C++库在处理过程中需要把结果、进度、异常等通知Java层(比如音视频解码完成后通知Java UI刷新)

2、需要用Java层的功能,比如C++代码需要用Java的网络、数据库、UI等功能,或者需要用Java写的业务逻辑。

总结

只要C++需要通知Java、调用Java功能、或实现回调机制时,就会用C++调用Java

实际开发中的配合

  • 很多时候,两者是配合使用的:

Java先调用C++做底层处理,C++处理完后再回调Java通知结果。

  • 例如:Android音视频播放器,Java层调用C++解码,解码完后C++回调Java刷新画面。

简要对比

场景

Java调用C++

C++调用Java

用C++库/高性能需求

需要底层/系统功能

需要回调/通知Java

用Java功能/逻辑

看这个表格就一目了然了。

好了再回到JNIEnv 。

正文

JNIEnv 是 Java Native Interface (JNI) 中的一个重要结构,代表 JNI 环境的指针。它提供了一组函数,用于在本地代码(C/C++)中与 Java 虚拟机(JVM)进行交互。

定义

JNIEnv 是一个指向结构体的指针,包含了 JNI 函数的指针表。通过这个指针,开发者可以调用 JNI 提供的各种功能,如创建对象、调用方法、访问字段等。

作用

  • 访问 Java 对象JNIEnv 允许本地代码访问 Java 对象的属性和方法。
  • 调用 Java 方法: 可以通过 JNIEnv 调用 Java 方法,包括静态方法和实例方法。
  • 处理异常JNIEnv 提供了处理 Java 异常的功能,可以检查和抛出异常。

示例

c++调用 java

java

// Example.java
public class Example {public void greet() {System.out.println("Hello from Java!");}
}

c/c++

#include <jni.h>
#include <stdio.h>int main() {JavaVM *jvm;             // Java 虚拟机JNIEnv *env;            // JNI 环境JavaVMInitArgs vm_args; // JVM 启动参数JavaVMOption options[1]; // JVM 选项// 设置 JVM 选项,指定类路径options[0].optionString = "-Djava.class.path=."; // 当前目录vm_args.version = JNI_VERSION_1_6; // JNI 版本vm_args.nOptions = 1; // 选项数量vm_args.options = options; // 选项数组vm_args.ignoreUnrecognized = 0; // 忽略未识别的选项// 创建 Java 虚拟机jint ret = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);if (ret != JNI_OK) {fprintf(stderr, "Failed to create JVM\\n");return 1;}// 查找 Example 类jclass cls = (*env)->FindClass(env, "Example");if (cls == NULL) {fprintf(stderr, "Failed to find Example class\\n");(*jvm)->DestroyJavaVM(jvm);return 1;}// 创建 Example 类的实例jobject obj = (*env)->AllocObject(env, cls);if (obj == NULL) {fprintf(stderr, "Failed to create Example object\\n");(*jvm)->DestroyJavaVM(jvm);return 1;}// 查找 greet 方法jmethodID mid = (*env)->GetMethodID(env, cls, "greet", "()V");if (mid == NULL) {fprintf(stderr, "Failed to find greet method\\n");(*jvm)->DestroyJavaVM(jvm);return 1;}// 调用 greet 方法(*env)->CallVoidMethod(env, obj, mid);// 销毁 Java 虚拟机(*jvm)->DestroyJavaVM(jvm);return 0;
}
代码解析


创建 JVM:   使用 JNI_CreateJavaVM 创建 Java 虚拟机,并获取 JNIEnv 指针。
查找类和方法:    使用 FindClass 和 GetMethodID 查找 Java 类和方法。
调用方法:      使用 CallVoidMethod 调用 Java 方法。

java调用c++

1. Java 层声明 native 方法

java

public class JniDemo {// 声明 native 方法public native String stringFromJNI();static {// 加载 native 库System.loadLibrary("myjni");}
}
2. 使用 javac 和 javah 生成头文件(Android Studio 会自动生成)

如果你用 Android Studio,可以直接写 C/C++ 代码,不需要手动用 javah。

3. C/C++ 层实现

C 代码(myjni.c):

#include <jni.h>JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_JniDemo_stringFromJNI(JNIEnv *env, jobject thiz) {return (*env)->NewStringUTF(env, "Hello from C!");
}

C++ 代码(myjni.cpp):

#include <jni.h>extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_JniDemo_stringFromJNI(JNIEnv *env, jobject thiz) {return env->NewStringUTF("Hello from C++!");
}
4. 配置 CMakeLists.txt 或 Android.mk

CMakeLists.txt 示例:

cmake_minimum_required(VERSION 3.4.1)
add_library(myjni SHARED myjni.c) # 或 myjni.cpp
find_library(log-lib log)
target_link_libraries(myjni ${log-lib})
5. 在 Java 层调用
JniDemo demo = new JniDemo();
String result = demo.stringFromJNI();
System.out.println(result); // 输出: Hello from C!
6. 关键点说明
  • Java 层用 native 关键字声明方法。
  • 用 System.loadLibrary("库名") 加载 so 库。
  • C/C++ 层方法名格式:Java_包名_类名_方法名,下划线分隔。
  • Android Studio 推荐用 CMake 管理 native 代码。

概念总结

准确的说法:

  • JNIEnv 实际上是一个指向结构体的指针(在 C 里是 JNIEnv*),它代表了本地线程(c/C++创建的线程)与 JVM 之间的接口。
  • 通过 JNIEnv*,本地代码可以调用大量 JNI 提供的函数(比如访问 Java 对象、调用 Java 方法、抛出异常等)。
  • 每个线程都有自己独立的 JNIEnv*,不能跨线程使用。

简化理解:

  • JNIEnv* 是 JNI 提供给本地代码与 JVM 交互的“桥梁”或“接口”。

这时候是不是懵了。。。。。。。。。。。。。。。。。。

“本地线程”

“本地线程”在 JNI(Java Native Interface)语境下,指的是运行本地(Native)代码的线程,也就是运行 C/C++ 代码的线程。

在 Android 或 Java 程序中,本地线程通常有两种来源:

1. Java 线程

  • 由 JVM 创建的线程(比如你在 Java 里 new Thread() 启动的线程)。
  • 这些线程天然和 JVM 关联,JNI 方法会自动获得对应的 JNIEnv* 指针。

2. 本地(Native)线程

  • 由 C/C++ 代码直接创建的线程(比如用 pthread_create 创建的线程)。
  • 这些线程不是 JVM 创建的,默认和 JVM 没有任何关联。
  • 如果本地线程需要访问 JVM(比如调用 Java 方法),必须先通过 AttachCurrentThread 把自己“附加”到 JVM,获得自己的 JNIEnv* 指针,使用完后再 DetachCurrentThread。

总结

  • 本地线程:指运行在本地代码(C/C++)中的线程,可能是 Java 启动的,也可能是 C/C++ 启动的。
  • JNIEnv* 与本地线程:每个本地线程(无论来源)都要有自己的 JNIEnv*,不能跨线程使用。

例子

// C/C++ 里创建线程
void* thread_func(void* args) {JNIEnv* env;// 需要先 attach 到 JVM(*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);// ... 使用 env 调用 Java 方法 ...(*jvm)->DetachCurrentThread(jvm);return NULL;
}

本地线程”与“Java线程”的区别

1. Java线程
  • 定义:由 JVM(Java 虚拟机)通过 Java 代码创建的线程,比如 new Thread() 或 Executors.newSingleThreadExecutor()。
  • 与 JVM 的关系:这些线程在创建时就已经和 JVM 关联,JVM 会自动为它们分配 JNIEnv*。
  • JNI 使用:在 Java 线程中调用 native 方法时,JNI 环境已经准备好,可以直接使用 JNIEnv*。

示例:

new Thread(() -> {// 这里是 Java 线程nativeMethod(); // 可以直接调用 JNI 方法
}).start();

2. 本地线程(Native Thread)

  • 定义:由本地代码(C/C++)直接创建的线程,比如用 pthread_create 或 Windows 的 CreateThread。
  • 与 JVM 的关系:这些线程不是 JVM 创建的,默认和 JVM 没有关联。
  • JNI 使用:如果本地线程需要访问 JVM(比如调用 Java 方法),必须先通过 AttachCurrentThread 将自己附加到 JVM,获得 JNIEnv*,用完后要 DetachCurrentThread。
void* thread_func(void* args) {JNIEnv* env;// 需要手动 attach(*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);// ... JNI 操作 ...(*jvm)->DetachCurrentThread(jvm);return NULL;
}// 创建本地线程
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);

3. 主要区别总结

对比项Java线程本地线程(Native Thread)
创建方式Java 代码(JVM 创建)C/C++ 代码(如 pthread_create)
与 JVM 关系自动关联,自动有 JNIEnv*默认无关联,需手动 attach
JNI 使用直接可用 JNIEnv*需 attach 后才有 JNIEnv*
生命周期管理JVM 管理需开发者手动管理

结论

  • Java线程:JVM 创建,自动有 JNI 环境,直接用。
  • 本地线程:C/C++ 创建,默认无 JNI 环境,需手动 attach/detach。

JNIEnv 实际上是一个指向结构体的指针(在 C 里是 JNIEnv*),它代表了本地线程(c/C++创建的线程)与 JVM 之间的接口。
这里为什么要说jvm?

这里说“JVM”是因为JNIEnv* 其实是本地代码(无论是 Java 线程还是 C/C++ 线程)与 Java 虚拟机(JVM)之间的桥梁。

详细解释

1. JVM 的作用
  • JVM(Java Virtual Machine)是运行 Java 程序的虚拟机,负责管理 Java 对象、内存、线程、类加载等。
  • 所有 Java 代码都运行在 JVM 之上,JVM 也负责管理和调度线程。
2. JNIEnv* 的作用
  • JNIEnv* 提供了一组函数指针,允许本地代码(C/C++)访问和操作 JVM 里的 Java 对象、类、方法等。
  • 你可以通过 JNIEnv* 调用 Java 方法、创建 Java 对象、抛出异常等,这些操作都需要 JVM 的支持。
3. 为什么强调“JVM”?
  • 因为 JNIEnv* 不是单纯让你操作 Java 线程或 Java 对象,而是让你通过 JNI 机制和 JVM 交互。
  • 只有 JVM 能理解和管理 Java 世界的所有内容,JNIEnv* 就是本地代码和 JVM 之间的“接口”或“桥梁”。
  • 不管线程是 Java 创建的还是 C/C++ 创建的,只要你想操作 Java 世界,都必须通过 JVM,而 JNIEnv* 就是你和 JVM 之间的“钥匙”。

总结

> 这里说“JVM”,是因为 JNIEnv* 让本地代码能够和 Java 虚拟机(JVM)进行交互,访问和操作 JVM 管理的所有 Java 资源。

好了,这里讲的够多了。先这样,后面继续讲。

相关知识:

JNI实现Android音视频播放器的设计方案-CSDN博客

c++ 的标准库 --- std:: -CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/87340.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/87340.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工智能训练师——智能语音识别ASR

人机对话——ASR 概念 ASR&#xff08;Automatic Speech Recognition&#xff0c;自动语音识别&#xff09;是一种将人类语音转换为文本的技术。它使得计算机能够“听”懂人类的语音指令或对话&#xff0c;并将其转换成可读、可编辑的文本形式。ASR技术是人机交互领域中的一个…

Compose 常用命令详解——AI教你学Docker

3.3 Compose 常用命令详解 Docker Compose 通过一系列命令高效管理多容器应用。理解这些命令&#xff0c;可以让你灵活地启动、停止、查看、调试、扩缩容和配置 Compose 项目。 一、核心命令详解 1. docker compose up 功能&#xff1a;启动并构建所有服务&#xff0c;生成网…

Mausezahn - 网络流量生成与测试工具(支持从链路层到应用层的协议模拟)

Mausezahn 是一个 网络流量生成与测试工具&#xff0c;主要用于模拟各种网络协议行为、测试网络设备性能、验证安全策略或进行故障排查。它支持从底层链路层&#xff08;如 Ethernet、VLAN&#xff09;到高层应用层&#xff08;如 HTTP、DNS&#xff09;的协议模拟&#xff0c;…

08-three.js Textures

Three.js Journey — Learn WebGL with Three.jsThe ultimate Three.js course whether you are a beginner or a more advanced developerhttps://threejs-journey.com/?c=p3 使用原生 JavaScript 首先是静态页面的放置位置,如果使用Vite模版配置,可以直接放在 /static/ …

git 仓库取消合并的分支

要取消 Git 仓库中某次特定的分支合并(例如第一次合并),同时保留其他分支的合并,需要通过 Git 的版本控制功能来操作。以下是具体的步骤和方法,假设你想撤销某次合并(例如某个提交),并确保其他合并不受影响: 背景假设 你有一个 Git 仓库,主分支(例如 main)上已经合…

【从历史数据分析英特尔该如何摆脱困境】

与大多数其他分析师不同&#xff0c;自2013年以来&#xff0c;笔者就一直在积极强调英特尔未来将遭遇冰山&#xff0c;最终我们预测英特尔将在试图执行其之前的战略时破产。尽管我们更愿意采用与英特尔不同的代工厂方法&#xff08;即与台积电成立合资企业&#xff09;&#xf…

【PyTorch】PyTorch中张量(Tensor)微分操作

PyTorch深度学习总结 第六章 PyTorch中张量(Tensor)微分操作 文章目录 PyTorch深度学习总结前言一、torch.autograd模块二、主要功能和使用方法1. 张量的 requires_grad 属性2. backward() 方法3. torch.no_grad() 上下文管理器三、函数总结前言 上文介绍了PyTorch中张量(Ten…

Rust 项目实战:Flappy Bird 游戏

Rust 项目实战&#xff1a;Flappy Bird 游戏 Rust 项目实战&#xff1a;Flappy Bird 游戏理解 Game loop开发库&#xff1a;bracket-libbracket-terminalCodepage 437导入 bracket-lib 创建游戏游戏的模式添加玩家添加障碍最终效果项目源码 Rust 项目实战&#xff1a;Flappy Bi…

Gin 中间件详解与实践

一、中间件的核心概念 定义 中间件是Web开发中非常重要的概念&#xff0c;它可以在请求到达最终处理函数之前或响应返回客户端之前执行一系列操作。Gin 框架支持自定义和使用内置的中间件&#xff0c;让你在请求到达路由处理函数前进行一系列预处理操作。 它是介于请求与响应处…

非接触式DIC测量系统:助力汽车研发与测试的创新技术应用

近年来&#xff0c;随着新能源汽车品牌的快速崛起&#xff0c;新车发布的节奏加快&#xff0c;层出不穷的新产品&#xff0c;给消费者带来了全新的使用体验。与此同时&#xff0c;变革的产品体验也让一些过往的汽车测试和评价标准变得不再适用&#xff0c;尤其是与过往燃油车型…

FOC学习笔记(7)锁相环(PLL)原理及其在电机控制中的应用

1. 锁相环(PLL)概述 锁相环&#xff08;Phase-Locked Loop, PLL&#xff09;是一种闭环控制系统&#xff0c;用于使输出信号的相位与输入参考信号的相位同步。PLL广泛应用于通信、电机控制、频率合成、时钟恢复等领域。在电机无传感器控制&#xff08;Sensorless Control&…

鸿蒙自定义相机的拍照页面

1、权限申请 "requestPermissions": [{"name": "ohos.permission.CAMERA","reason": "$string:reason_camera","usedScene": {"abilities": ["EntryAbility"]}},{"name": "oh…

greenplum7.2并行备份及恢复

1.并行备份 pg_dump -Fd --gp-syntax -U gpadmin -p 5432 -h 172.19.0.2 -d postgres -j 4 -f /opt/greenplum/data/postgres_backup_$(date %Y-%m-%d) 参数 含义 -Fd 使用 directory 格式&#xff08;支持并行&#xff09; --gp-syntax 使用 Greenplum 特定语法&#xff08;…

备赛2025年初中古诗文大会:练习历年真题,吃透知识点(0703)

初中古诗文大会的比赛内容古诗词、文言文各占比50%左右&#xff0c;从历年的比赛来看&#xff0c;中考语文的古诗文部分&#xff08;35分&#xff09;涉及到的古诗词、文言文知识点都在初中古诗文大会中考过。这些知识点掌握了&#xff0c;对于将来高中、高考也有直接的帮助。 …

BRAKER:真核微生物cds和蛋白注释

https://github.com/Gaius-Augustus/BRAKER 安装 # 第一次打开会pull这个docker docker run --user 1000:100 --rm -it teambraker/braker3:latest bash bash /opt/BRAKER/example/docker-tests/test3.sh braker.gtf&#xff1a;BRAKER 的最终基因集。 braker.codingseq&am…

基于 Three.js 与 WebGL 的商场全景 VR 导航系统源码级解析

本文面向Web前端开发者、WebGL/Three.js 爱好者、对VR/AR应用开发感兴趣的技术人员、智慧商场解决方案开发者。详细介绍如何利用 WebGL (Three.js框架) 构建高性能的商场全景VR环境&#xff0c;并实现精准的室内定位与3D路径规划导航功能。 如需获取商场全景VR导航系统解决方案…

AWS CloudFormation部署双可用区VPC网络架构 - 完整指南

一、模板概述 本CloudFormation模板用于在AWS上快速部署一个高可用的双可用区VPC网络架构,包含公有子网和私有子网。该架构是构建云原生应用的基础,特别适合生产环境使用。 二、完整模板代码 AWSTemplateFormatVersion: 2010-09-09 Description: Customizable dual-AZ VPC…

2025汽车声学升级:高透音汽车喇叭网成高端车型新标配

随着消费者对车载音质和静谧性要求的提升&#xff0c;高透音汽车喇叭网正成为高端车型的差异化配置。传统冲压金属网因声学损耗大、设计单一逐渐被淘汰&#xff0c;而新一代蚀刻工艺通过微孔结构优化&#xff0c;实现了声学性能与美学设计的双重突破。以下是技术趋势与市场前景…

决策树(Decision tree)算法详解(ID3、C4.5、CART)

文章目录 一、决策树介绍1.1 决策树的结构特征1.2 决策树的构建三步骤1.3 决策树构建例子 二、ID3决策树&#xff1a;基于信息增益的决策模型2.1 信息增益的公式与符号解析2.2 信息增益的意义2.3 ID3决策树案例演示&#xff1a;贷款申请分类2.4 ID3决策树缺陷 三、C4.5决策树&a…

python基础-网络的TCP、UDP协议操作

1.tcp基本语法 # ### TCP协议 客户端 import socket # 1.创建一个socket对象 sk socket.socket() # 2.与服务端建立连接 sk.connect( ("127.0.0.1" , 9000) ) # 3.收发数据的逻辑 """发送的数据类型是二进制字节流""" ""&q…