JNIEnv* getEnv() noexcept
JNIEnv* env = androidJNIEnv.get();
jassert (env != nullptr); // <--------- assertion failure
return env;
The message it posts never gets to the Java side, so it looks like the assertion may be causing the crash.
Is there a reason why a Singleton or SharedResourcePointer derived from ChangeBroadcaster might return null for JNIEnv?
Just ran into this error again in a different app. I meant to send an example project to highlight the issue. I will when I get the time (probably after the ADC now!)
My current assumption is that it is to do with threading. Both apps are using React Native, which employs multiple threads.
Yes that would make perfect sense. If you have a thread created by something other than JUCE, then you need to call setEnv
on the thread before using any JUCE code. See the comment in juce_android_JNIHelper.h
:
// You should rarely need to use this function. Only if you expect callbacks
// on a java thread which you did not create yourself.
extern void setEnv (JNIEnv* env) noexcept;
I’m using JUCE with some other code that creates it’s own thread and would like those threads to be able to post updates to the JUCE GUI. (This is all C++ code, no Java). I’m running into the issue above when trying to run on Android. What do I need to pass to the setEnv() call made by own thread? I tried cacheing the result of getEnv() from the JUCE GUI thread and then using this later as the parameter to setEnv() in the other thread … it makes it pass the Assert() here, but ends up failing anyway. Any ideas would be very helpful
No, every thread has it’s own JNIEnv pointer so you can’t re-use one from another thread.
There are two ways to get a JNIEnv pointer:
In most cases you are currently executing native code because somewhere up the call-stack java code invoked some native method. Check your call-stack if this is the case. The JNI pointer will be supplied to you automatically by java: it is passed as the first argument to the C++ entry point for the native method that java called. You need to somehow store it there (or call just setEnv
there if you can).
The native code launched a new thread via pthreads or forking. If this is the case, then you need to create a new JNIEnv pointer via JNI’s AttachCurrentThread
. AttachCurrentThread
needs a JavaVM
pointer which can be shared between different threads. You can get the JavaVM
pointer on any thread that already has it’s own JNIEnv via JNI’s GetJavaVM
method.
Thanks! My case is #2 and I will try that. Meanwhile I was able to wrap a Juce Thread around my own cross-platform threading code class as a work-around in a pretty clean fashion. I’m currently debugging some deadlock issue that seems to be occurring where my native thread (now a Juce thread) is calling triggerAsyncUpdate() as a cue to the GUI thread component to update some displayed content … it’s only happening on Android (update - solved that issue)