简体   繁体   English

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

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

TL;DR; TL;博士; I'm having a problem with passing my FFMPEG raw data from C++ code to JAVA code, for displaying, through a thread.我在将我的 FFMPEG 原始数据从 C++ 代码传递到 JAVA 代码以通过线程显示时遇到问题。

There is a server set up that sends out encoded frames to its clients.有一个服务器设置向其客户端发送编码帧。 Those encoded frames are encoded with some FFMPEG magic.这些编码帧是用一些 FFMPEG 魔法编码的。 When received on client side, the fore-mentioned frames are getting decoded into raw RGB data (as a unsigned char *).在客户端收到时,上述帧将被解码为原始 RGB 数据(作为无符号字符 *)。 The problem now is that frames are being received in a "listener" of sorts.现在的问题是帧是在某种“侦听器”中接收的。 Just a thread running in the background polling the server and running specific onFrame function once a new frame is available.只是一个在后台运行的线程轮询服务器并在新帧可用时运行特定的onFrame function。

The current solution for displaying the frames in a video-format is to save each frame to internal storage in C++, and then have a FileObserver on java side that displays an image as soon as it's written in the memory.当前以视频格式显示帧的解决方案是将每个帧保存到 C++ 的内部存储中,然后在 java 端有一个FileObserver ,一旦它写入 ZCD69B4957F06CD8198D7 就显示图像Sadly, that approach yields a 6 FPS video on phone, for a 10 FPS video from Server.可悲的是,这种方法在手机上产生了 6 FPS 的视频,而来自服务器的视频则为 10 FPS。

I need a way of passing that unsigned char * (jbytearray) to my JAVA code so I can decode it and display it from RAM rather than Disk.我需要一种将 unsigned char * (jbytearray) 传递给我的 JAVA 代码的方法,这样我就可以对其进行解码并从 RAM 而不是磁盘显示它。

It's worth mentioning that onFrame function cannot have JNIEnv* && jobject inside it's arguments list (Library requirements).值得一提的是, onFrame function 的 arguments 列表中不能有JNIEnv* && jobject (库要求)。

What I have tried so far is making a native method in my MainActivity through which I pass JNIEnv and jobject and assign those to global variables到目前为止,我尝试的是在我的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);

After that, in my onFrame I call之后,在我的onFrame我打电话
jvm->AttachCurrentThread(&m_globalEnv, NULL);
And then I try to call a JAVA method from somewhere inside the code (It's irrelevant where/when in the onFrame I call it) by doing:然后我尝试通过执行以下操作从代码内部的某处调用 JAVA 方法(这与我在onFrame中调用它的位置/时间无关):

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

And then all crashes with either:然后所有崩溃:

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
.
.
.

EDIT 1编辑 1

After Trying out the code from Michael's solution, I got the在尝试了迈克尔解决方案中的代码后,我得到了
java_vm_ext.cc:542] JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc94f7f8c error. java_vm_ext.cc:542] JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc94f7f8c错误。 After running the app in debug mode to catch the error, I got to the jni.h ;在调试模式下运行应用程序以捕获错误后,我进入了jni.h Line of code that triggers the error is: m_env->CallVoidMethod(m_globalObject, testMethod, 5);触发错误的代码行是: m_env->CallVoidMethod(m_globalObject, testMethod, 5); (5 being the number I am trying to pass for testing purposes). (5 是我试图通过用于测试目的的数字)。 The line of code inside jni.h that is being highlighted by the debugger is inside of jni.h 中由调试器突出显示的代码行位于
void CallVoidMethod(jobject obj, jmethodID methodID, ...)
and it's functions->CallVoidMethodV(this, obj, methodID, args);它的functions->CallVoidMethodV(this, obj, methodID, args);
which is defined on line 228: void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);在第 228 行定义: void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);

I see two potential issues with the code:我看到代码有两个潜在问题:

1. Sharing a JNIEnv* across threads 1. 跨线程共享JNIEnv*
Each native thread should obtain its own JNIEnv* by attaching itself to the JVM, and then detaching itself at some point.每个本机线程都应该通过将自身附加到 JVM 并在某个时候将自身分离来获得自己的JNIEnv* See this answer for more details and possible solutions.有关更多详细信息和可能的解决方案,请参阅此答案

2. Caching local references 2.缓存本地引用
The thiz reference you receive as the second argument to a native function is a local reference, as are most of the jobject s returned from calling JNI functions.您作为本机 function 的第二个参数收到的thiz引用是本地引用,调用 JNI 函数返回的大多数jobject也是本地引用。
A local reference is usable only "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" .本地引用只能“从它最初交给的线程中使用,并且在显式调用 DeleteLocalRef() 或更常见的情况下,直到您从本机方法返回之前有效”

If you want to use that object from another thread you need to create a global reference from the local reference:如果你想从另一个线程使用 object,你需要从本地引用创建一个全局引用:

m_globalObject = NewGlobalRef(thiz);

Remember to delete the global reference ( DeleteGlobalRef(m_globalObject) ) when you no longer need to use that object anywhere in your native code.当您不再需要在本机代码中的任何位置使用 object 时,请记住删除全局引用 ( DeleteGlobalRef(m_globalObject) )。 Otherwise you may cause a memory leak.否则可能会导致 memory 泄漏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM