日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

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

發布時間:2025/6/17 c/c++ 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Android FFMPEG 开发】C++ 回调 Java 方法 模板 ( JavaVM *vm | JNIEnv *env | jobject instance | 引用类型 | 模板代码示例 ) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

        • I . Native 調用 Java 方法
        • II . JNIEnv *env 與 jobject instance
        • III . JavaVM *vm
        • IV . 局部引用 與 全局引用 分析
        • V . Native 調用 Java 方法 ( 主線程 )
        • VI . Native 調用 Java 方法 ( 子線程 )
        • VII . Java 層方法
        • VIII . C++ Java 調用助手類 ( JavaCallHelper.h 頭文件 )
        • IX . C++ Java 調用助手類 ( JavaCallHelper.cpp )
        • X . Native 入口 C++ 方法



I . Native 調用 Java 方法



1 . 前置知識點 : 參考 【Android NDK 開發】JNI 方法解析 ( C/C++ 調用 Java 方法 | 函數簽名 | 調用對象方法 | 調用靜態方法 ) 博客內容 , 了解如何在 C++ 中調用 Java 方法 ;


2 . Native 調用 Java 方法 流程如下 :


① 獲取函數簽名 : 查找字節碼文件 , 使用 javap 獲取函數簽名 ;

② 反射獲取 Java 方法 : 通過調用 jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) 方法獲取方法 ID ;

③ 調用 Java 方法 : 通過調用 void CallXxxMethod(jobject obj, jmethodID methodID, …) 方法 , 調用 Java 方法 ;



II . JNIEnv *env 與 jobject instance



1 . 調用 Java 方法所需參數 : 調用 Java 方法需要 JNIEnv *env 參數 和 對應的 jobject instance Java 類參數 ;


① JNIEnv *env : JNI 環境 , 注意子線程的 JNI 環境需要獲取 , 主線程的 JNI 環境可以直接從 Native 層實現的 Java 方法中獲取 ;

② jobject instance : 在 Native 層的 Java 對象 ;


2 . 主線程 JNIEnv *env 和 jobject instance 獲取方法 : 這兩個值都可以在 C++ 中實現的 native 方法中獲取 ;

extern "C" JNIEXPORT void JNICALL Java_kim_hsl_ffmpeg_Player_native_1prepare(JNIEnv *env, jobject instance, jstring dataSource_){ ... }

上面的 C++ 方法是實現的 kim.hsl.ffmpeg.Player 類的 native void native_prepare(String dataSource) 方法 ;


3 . 子線程 JNIEnv *env 獲取方法 : 需要使用 JavaVM *vm 獲取 , 即 Java 虛擬機參數 ; 獲取流程如下 :


① 聲明子線程 JNIEnv* 指針 ;

② Java 虛擬機 調用附加線程的方法 ;

//子線程 : 需要通過 JavaVM * 獲取該子線程的 JNIEnv *JNIEnv *env_thread;//Java 虛擬機 調用附加線程的方法 , 可以獲取當前線程的 JNIEnv* 指針vm->AttachCurrentThread(&env_thread, 0);

III . JavaVM *vm



JavaVM *vm 獲取方法 : 在 JNI_OnLoad() 方法中獲取 ;

//JNI_OnLoad 中獲取的 Java 虛擬機對象放在這里 JavaVM *javaVM; int JNI_OnLoad(JavaVM *vm, void *r){javaVM = vm;return JNI_VERSION_1_6; }

JNI_OnLoad 參考 : 【Android NDK 開發】JNI 動態注冊 ( 動態注冊流程 | JNI_OnLoad 方法 | JNINativeMethod 結構體 | GetEnv | RegisterNatives ) II . JNI_OnLoad 方法



IV . 局部引用 與 全局引用 分析



1 . 局部引用 與 全局引用 : JavaVM *vm , JNIEnv *env 與 jobject instance 是在方法中獲取的 , 如果跨線程調用 , 就需要考慮其引用的類型 , 局部引用 或 全局引用 ;


① 局部引用 : 方法結束后便不能使用了 ;

② 全局引用 : 可以跨方法 , 跨線程調用 ;


2 . 全局引用 : JNIEnv *env 與 JavaVM *vm 本身就是全局引用 , 不用刻意將其轉為全局引用 , 可以跨方法跨線程調用 ;


3 . 局部引用 : jobject instance 是 Java_kim_hsl_ffmpeg_Player_native_1prepare 方法中的局部引用 , 如果要跨方法 , 跨線程調用 , 需要將其轉為全局引用 ;


4 . 示例解析 : 在下面的構造方法中可以看到 , 針對 JNIEnv *env 與 JavaVM *vm , 沒有經過任何處理 , 直接記錄下來 , 就可以在其它任何方法 , 任何線程中調用 , 但是 jobject instance Java 對象 , 必須將其轉為全局引用 , 才能在其它方法或線程中調用 ;


5 . 參考 :


① 局部引用 : 【Android NDK 開發】JNI 引用 ( 局部引用 | 局部引用作用域 | 局部引用產生 | 局部引用釋放 | 代碼示例)

② 全局引用 : 【Android NDK 開發】JNI 引用 ( 全局引用 | NewGlobalRef | DeleteGlobalRef )

③ 弱全局引用 : 【Android NDK 開發】JNI 引用 ( 弱全局引用 | NewWeakGlobalRef | DeleteWeakGlobalRef )



V . Native 調用 Java 方法 ( 主線程 )



主線程中可以直接使用 Native 方法中獲取的 JNIEnv *env 調用 Java 方法 ;

//主線程 : 可以直接使用 JNIEnv * 指針env->CallVoidMethod(instance, onErrorId, errorCode);

VI . Native 調用 Java 方法 ( 子線程 )



子線程需要通過 JavaVM * 獲取該子線程的 JNIEnv * , 然后通過子線程的 JNIEnv * 調用 Java 方法 ;

//子線程 : 需要通過 JavaVM * 獲取該子線程的 JNIEnv *JNIEnv *env_thread;//Java 虛擬機 調用附加線程的方法 , 可以獲取當前線程的 JNIEnv* 指針vm->AttachCurrentThread(&env_thread, 0);//調用 Java 方法env_thread->CallVoidMethod(instance, onErrorId, errorCode);//解除線程附加vm->DetachCurrentThread();

參考 : 【Android NDK 開發】JNI 線程 ( JNI 線程創建 | 線程執行函數 | 非 JNI 方法獲取 JNIEnv 與 Java 對象 | 線程獲取 JNIEnv | 全局變量設置 )



VII . Java 層方法



package kim.hsl.ffmpeg;import android.util.Log;/*** Java 層與 Native 層交互 接口*/ public class Player implements SurfaceHolder.Callback {private static final String TAG = "Player";// 加載動態庫static {System.loadLibrary("native-lib");}/*** C++ 層錯誤回調函數* @param errorCode*/public void onError(int errorCode){Log.i(TAG, "出現錯誤 錯誤碼 : " + errorCode);}/*** C++ 中 prepare 時回調該方法*/public void onPrepare(){Log.i(TAG, "準備完畢 onPrepare");}native void native_prepare(String dataSource);}

VIII . C++ Java 調用助手類 ( JavaCallHelper.h 頭文件 )



// // Created by octop on 2020/3/2. // 作用 : 在 C/C++ 層調用 Java 層函數的幫助類 // 反射 Java 類 , 并調用其方法 //#ifndef INC_011_FFMPEG_JAVACALLHELPER_H #define INC_011_FFMPEG_JAVACALLHELPER_H#include <jni.h>class JavaCallHelper {public://構造方法JavaCallHelper(JavaVM *vm, JNIEnv *env, jobject instance);//析構方法~JavaCallHelper();//錯誤回調方法 , 通過該方法回調錯誤信息給 Java 層void onError(int thread, int errorCode);//準備回調方法void onPrepare(int thread);private:/** 跨線程相關 :* JNIEnv * 是不能跨線程使用的* 如果在線程中反射調用 Java 方法* 必須重新獲取對應線程的 JNIEnv *env*/JavaVM *vm;JNIEnv *env;jobject instance;//onError 方法對應的 方法 IDjmethodID onErrorId;//onPrepare 方法對應的 方法 IDjmethodID onPrepareId;};#endif //INC_011_FFMPEG_JAVACALLHELPER_H

IX . C++ Java 調用助手類 ( JavaCallHelper.cpp )



// // Created by octop on 2020/3/2. //#include "JavaCallHelper.h"JavaCallHelper::JavaCallHelper(JavaVM *vm, JNIEnv *env, jobject instance) {/** 如果在子線程調用 Java 方方法* 需要借助 JavaVM * vm , 獲取子線程的 JNIEnv *env 進行反射調用** 如果在主線程調用 Java 方法* 可以直接調用主線程傳入的 JNIEnv *env 進行反射調用** 注意 : jobject 如果要跨方法 , 跨線程調用 , 需要創建全局引用 , 不要使用局部引用*/this->vm = vm;this->env = env;this->instance = env->NewGlobalRef(instance);//初始化 onError 方法反射信息jclass clazz = env->GetObjectClass(instance);//Java 中對應的方法 public void onError(int errorCode)this->onErrorId = env->GetMethodID(clazz, "onError", "(I)V");//Java 中對應的 public void onPrepare()this->onPrepareId = env->GetMethodID(clazz, "onPrepare", "()V");}JavaCallHelper::~JavaCallHelper() {//釋放全局引用env->DeleteGlobalRef(instance);}/*** 判斷 thread 是否是主線程* 如果是主線程 :* 如果是子線程 :*** @param thread* @param errorCode*/ void JavaCallHelper::onError(int thread, int errorCode) {if(thread == 1){//主線程 : 可以直接使用 JNIEnv * 指針this->env->CallVoidMethod(instance, onErrorId, errorCode);}else{//子線程 : 需要通過 JavaVM * 獲取該子線程的 JNIEnv *JNIEnv *env_thread;//Java 虛擬機 調用附加線程的方法 , 可以獲取當前線程的 JNIEnv* 指針vm->AttachCurrentThread(&env_thread, 0);//調用 Java 方法env_thread->CallVoidMethod(instance, onErrorId, errorCode);//解除線程附加vm->DetachCurrentThread();}}void JavaCallHelper::onPrepare(int thread) {if(thread == 1){//主線程 : 可以直接使用 JNIEnv * 指針this->env->CallVoidMethod(instance, onPrepareId);}else{//子線程 : 需要通過 JavaVM * 獲取該子線程的 JNIEnv *JNIEnv *env_thread;//Java 虛擬機 調用附加線程的方法 , 可以獲取當前線程的 JNIEnv* 指針vm->AttachCurrentThread(&env_thread, 0);//調用 Java 方法env_thread->CallVoidMethod(instance, onPrepareId);//解除線程附加vm->DetachCurrentThread();}}

X . Native 入口 C++ 方法



#include <jni.h> #include <string> #include "FFMPEG.h"//聲明 FFMPEG 類 FFMPEG *ffmpeg = 0;//JNI_OnLoad 中獲取的 Java 虛擬機對象放在這里 JavaVM *javaVM; int JNI_OnLoad(JavaVM *vm, void *r){javaVM = vm;return JNI_VERSION_1_6; }extern "C" JNIEXPORT void JNICALL Java_kim_hsl_ffmpeg_Player_native_1prepare(JNIEnv *env, jobject instance, jstring dataSource_) {//創建 Java 調用類JavaCallHelper * javaCallHelper = new JavaCallHelper(javaVM, env, instance);//調用 Java 層的 onPrepare 方法callHelper->onPrepare(2); }

總結

以上是生活随笔為你收集整理的【Android FFMPEG 开发】C++ 回调 Java 方法 模板 ( JavaVM *vm | JNIEnv *env | jobject instance | 引用类型 | 模板代码示例 )的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。