重新認識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
英文地址,請注明出處:http://en.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…