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



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

设置了一个服务器,用于向客户端发送编码帧。这些编码帧是用一些FFMPEG魔术进行编码的。当在客户端接收时,前面提到的帧被解码为原始RGB数据(作为无符号字符*(。现在的问题是,帧是在一个"信道"中被接收的;听众";在某种程度上。只是一个在后台运行的线程轮询服务器,并在新帧可用时运行特定的onFrame函数。

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

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

值得一提的是,CCD_ 3函数不能有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 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也是本地引用
本地引用只能从最初交给它的线程"em>"中使用,并且在显式调用DeleteLocalRef((之前有效,或者更常见的情况是,在您从本机方法"返回之前有效

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

m_globalObject = NewGlobalRef(thiz);

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

最新更新