【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 | 引用类型 | 模板代码示例 )相关推荐
- 【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )
文章目录 I . FFMPEG ANativeWindow 原生绘制 II . FFMPEG 原生绘制流程 III . Java 层获取 Surface 画布 IV . 传递 Surface 画布到 ...
- 安卓开发——JNI——回调java中的方法
JNI开发中 在C代码中回调java中的方法 package com.example.jnitest2;import android.app.Activity; import android.cont ...
- 【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( 设置 ANativeWindow 缓冲区属性 | 获取绘制缓冲区 | 填充数据到缓冲区 | 启动绘制 )
文章目录 I . FFMPEG ANativeWindow 原生绘制 前置操作 II . FFMPEG 原生绘制流程 III . 设置 ANativeWindow 绘制窗口属性 ANativeWind ...
- 【Android FFMPEG 开发】音视频基础 和 FFMPEG 编译 ( 音视频基础 | MPEG-4 标准 | Android 开发环境 | FFMPEG 交叉编译 | 安卓项目导入配置 )
本篇博客代码及资源下载 : https://download.csdn.net/download/han1202012/10382762 文章目录 一. 音视频基础 1. 音频基础 (1) 声音要素 ...
- JNI中创建新的线程回调java方法的技巧
在实际项目中,经常需要在Native层创建新的线程处理一些耗时操作,然后将结果回调给java层.如果按照普通的方式,直接获取MethodID,然后新线程中调用CallxxxMethod(),这样肯定是 ...
- 【Android FFMPEG 开发】OpenSLES 播放音频 ( 创建引擎 | 输出混音设置 | 配置输入输出 | 创建播放器 | 获取播放/队列接口 | 回调函数 | 开始播放 | 激活回调 )
文章目录 I . FFMPEG 播放视频流程 II . OpenSLES 播放音频流程 III . OpenSLES 播放参考 Google 官方示例 IV . OpenSL ES 播放代码 ( 详细 ...
- 【Android FFMPEG 开发】Android 中使用 FFMPEG 对 MP3 文件进行混音操作
文章目录 一.前置操作 ( 移植 FFMPEG ) 二.FFMPEG 混音命令 三.Android FFMPEG 混音源代码完整示例 四.博客源码 一.前置操作 ( 移植 FFMPEG ) 参考 [A ...
- 【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )
文章目录 博客简介 . FFMPEG 编解码器获取流程 I . FFMPEG 获取音视频流的编解码参数 AVCodecParameters *codecpar II . FFMPEG 查找解码器 av ...
- 【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )
文章目录 I . FFMPEG 播放视频流程 II . FFMPEG 音频重采样流程 III . FFMPEG 音频重采样 IV . FFMPEG 初始化音频重采样上下文 SwrContext V . ...
最新文章
- python答题系统的代码_Python考试系统自动答题(教务处)
- R语言PCA主成分分析(Principle Component Analysis)实战2
- springboot + shiro 尝试登录次数限制与并发登录人数控制
- 单体应用架构——垂直应用架构———分布式架构———SOA架构———微服务架构
- 安装bigdesk后es无法启动_安装天正后,探索者无法双击启动?
- MATLAB(五)在线性代数中的应用
- OpenCV中的仿射变换
- Win10怎么打开或关闭自动维护功能
- 我们常说的CDN到底是什么?
- 约瑟夫问题 c语言数组,约瑟夫问题的数组实现
- 计算机文化基础(高职高专版 第十一版)第一章答案
- 下个五年,跨境支付的变数在哪里?
- eclipse hadoop1.2.0配置及wordcount运行
- ios王者荣耀更新服务器维护,王者荣耀苹果更新不了 苹果无法进行版本更新如何解决...
- JSP入门教程:JSP简明教程
- 张一鸣宣布卸任字节跳动CEO
- 分布式计算框架(四) 计算节点模块
- 软件从业者成功的秘密
- amdgpu_fence_emit 的原罪-- 完成fence
- mysql proxies priv_Mysql 5.7.18 运用MySQL proxies_priv完成类似用户组管理案例分享