java - 如何从 JNI 中的线程内部调用 JAVA 方法

标签 java android c++ ffmpeg java-native-interface

长话短说;我在通过线程将我的 FFMPEG 原始数据从 C++ 代码传递到 JAVA 代码以进行显示时遇到问题。

有一个服务器设置发送编码帧到它的客户端。这些编码帧是用一些 FFMPEG 魔法编码的。当在客户端接收时,上述帧被解码为原始 RGB 数据(作为 unsigned char *)。现在的问题是帧正在以某种“监听器”的形式接收。只是在后台运行的线程轮询服务器并在新帧可用时运行特定的 onFrame 函数。

当前以视频格式显示帧的解决方案是在 C++ 中将每个帧保存到内部存储器,然后在 java 端有一个 FileObserver,它在写入图像后立即显示图像在内存中。遗憾的是,对于来自服务器的 10 FPS 视频,该方法在手机上产生 6 FPS 视频。

我需要一种将 unsigned char * (jbytearray) 传递到我的 JAVA 代码的方法,这样我就可以解码它并从 RAM 而不是磁盘显示它。

值得一提的是,onFrame 函数的参数列表中不能包含 JNIEnv* && jobject(库要求)。

到目前为止我尝试的是在我的 MainActivity 中创建一个本地方法,我通过它传递 JNIEnvjobject 并将它们分配给全局变量

JNIEnv* m_globalEnv = env;
jobject m_globalObject = thiz;
JavaVM m_jvm = 0;
jclass mainActivity = m_globalEnv->GetObjectClass(m_globalObject);
jmethodID testMethod = m_globalEnv->GetMethodID(mainClass, "testMethod", "(I)V");

m_globalEnv->GetJavaVM(&m_jvm);

之后,在我的 onFrame 中调用
jvm->AttachCurrentThread(&m_globalEnv, NULL);
然后我尝试从代码中的某处调用 JAVA 方法(这与我在 onFrame 中调用它的位置/时间无关)通过执行以下操作:

m_globalEnv->CallVoidMethod(m_globalObject, "testMethod", 5);

然后所有崩溃都发生在:

1- JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xffe8ea7c
2- JNI DETECTED ERROR IN APPLICATION: Thread is making JNI calls without being attached
.
.
.

编辑 1

在尝试了 Michael 解决方案中的代码后,我得到了
java_vm_ext.cc:542] JNI 检测到应用程序错误:使用无效的 jobject 0xc94f7f8c 错误。 在 Debug模式下运行应用程序以捕获错误后,我到达了 jni.h;触发错误的代码行是: m_env->CallVoidMethod(m_globalObject, testMethod, 5); (5 是我为测试目的而尝试传递的数字)。 调试器突出显示的 jni.h 中的代码行位于
void CallVoidMethod(jobject obj, jmethodID methodID, ...)
这是 函数->CallVoidMethodV(this, obj, methodID, args);
在第 228 行定义: void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_​​list);

最佳答案

我发现代码有两个潜在问题:

<强>1。跨线程共享JNIEnv*
每个 native 线程都应该通过将自身附加到 JVM 来获得自己的 JNIEnv*,然后在某个时候分离自身。参见 this answer了解更多详情和可能的解决方案。

<强>2。缓存本地引用
作为 native 函数的第二个参数收到的 thiz 引用是本地引用,调用 JNI 函数返回的大多数 jobject 也是如此。
本地引用只能“从它最初传递给的线程中使用,并且在显式调用 DeleteLocalRef() 之前有效,或者更常见的是,直到您从本地方法返回之前”。< br/>

如果您想从另一个线程使用该对象,您需要从本地引用创建一个全局引用:

m_globalObject = NewGlobalRef(thiz);

当您不再需要在 native 代码中的任何位置使用该对象时,请记住删除全局引用 (DeleteGlobalRef(m_globalObject))。否则可能会导致内存泄漏。

关于java - 如何从 JNI 中的线程内部调用 JAVA 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58875780/

相关文章:

java - SQLite与android,如何连接到在线数据库?

java - 无法在 Spring Boot 2.1.1.RELEASE 中访问消息源

android - 在 Android Studio 中打开整个模块文件树

android - 如何从我的应用程序以编程方式打开带有更新页面的Playstore应用程序

c++ - SDL/TTF : TTF_RenderText_Blended_Wrapped returns NULL

c++ - 我不明白为什么我声明一个空的内部结构并将其传递给构造函数

java - 独立的 Java 应用程序

java - Android Studio java 文件损坏

android - 非 Activity 类中的改造调用

c++ - STL 列表添加和删除所选元素