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

How To Call JAVA Methods from inside of a Thread in JNI

TL;DR;我在将我的 FFMPEG 原始数据从 C++ 代码传递到 JAVA 代码以通过线程显示时遇到问题。

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

当前以视频格式显示帧的解决方案是将每个帧保存到 C++ 的内部存储器中,然后在 java 一侧有一个 FileObserver 尽快显示图像因为它写在内存中。遗憾的是,对于来自服务器的 10 FPS 视频,这种方法在 phone 上产生了 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 中的 where/when 无关)通过执行以下操作:

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 DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc94f7f8c 错误。 在 运行 处于调试模式的应用程序捕获错误后,我到达了 jni.h;触发错误的代码行是: m_env->CallVoidMethod(m_globalObject, testMethod, 5); (5 是我为测试目的而尝试传递的数字)。 调试器突出显示的 jni.h 内的代码行位于
void CallVoidMethod(jobject obj, jmethodID methodID, ...)
这是 functions->CallVoidMethodV(this, obj, methodID, args);
在第 228 行定义: void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);

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

1.跨线程共享 JNIEnv*
每个本机线程都应该通过将自己附加到 JVM 来获得自己的 JNIEnv*,然后在某个时候分离自己。有关详细信息和可能的解决方案,请参阅

2。缓存本地引用
作为本机函数的第二个参数收到的 thiz 引用是本地引用,调用 JNI 函数返回的大多数 jobject 也是如此。
本地引用仅可用 "from the thread it was originally handed to, and is valid until either an explicit call to DeleteLocalRef() or, more commonly, until you return from your native method".

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

m_globalObject = NewGlobalRef(thiz);

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