1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

JNI: Add WeakGlobalRef helper type

This commit is contained in:
reuk 2023-11-28 19:13:22 +00:00
parent 8ce1f19bf0
commit d64b9e7782
No known key found for this signature in database
3 changed files with 75 additions and 36 deletions

View file

@ -248,7 +248,7 @@ struct SystemJavaClassComparator
//==============================================================================
JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const uint8* bc, size_t n)
: classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (nullptr)
: classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK)
{
SystemJavaClassComparator comparator;
@ -375,7 +375,7 @@ void JNIClassBase::initialise (JNIEnv* env, jobject context)
}
if (classRef == nullptr)
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
classRef = GlobalRefImpl { LocalRef { env->FindClass (classPath) } };
jassert (classRef != nullptr);
initialiseFields (env);
@ -388,23 +388,22 @@ void JNIClassBase::tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoa
// Android SDK <= 19 has a bug where the class loader might throw an exception but still return
// a non-nullptr. So don't assign the result of this call to a jobject just yet...
auto classObj = env->CallObjectMethod (classLoader, JavaClassLoader.loadClass, classNameAndPackage.get(), (jboolean) true);
LocalRef classObj { (jclass) env->CallObjectMethod (classLoader, JavaClassLoader.loadClass, classNameAndPackage.get(), (jboolean) true) };
if (jthrowable exception = env->ExceptionOccurred())
{
env->ExceptionClear();
classObj = nullptr;
classObj = {};
}
// later versions of Android don't throw at all, so re-check the object
if (classObj != nullptr)
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (classObj));
classRef = GlobalRefImpl { classObj };
}
void JNIClassBase::release (JNIEnv* env)
{
if (classRef != nullptr)
env->DeleteGlobalRef (classRef);
classRef.clear (env);
}
void JNIClassBase::initialiseAllClasses (JNIEnv* env, jobject context)

View file

@ -270,7 +270,7 @@ private:
size_t byteCodeSize;
int minSDK;
jclass classRef = nullptr;
GlobalRefImpl<jclass> classRef;
static Array<JNIClassBase*>& getClasses();
void initialise (JNIEnv*, jobject context);

View file

@ -48,7 +48,7 @@ DECLARE_JNI_CLASS (AndroidResolveInfo, "android/content/pm/ResolveInfo")
//==============================================================================
JavaVM* androidJNIJavaVM = nullptr;
jobject androidApkContext = nullptr;
GlobalRef androidApkContext;
//==============================================================================
JNIEnv* getEnv() noexcept
@ -105,6 +105,56 @@ extern "C" jint JNIEXPORT JNI_OnLoad (JavaVM* vm, void*)
return JNI_VERSION_1_2;
}
//==============================================================================
class WeakGlobalRef
{
public:
WeakGlobalRef() = default;
explicit WeakGlobalRef (jobject o) : WeakGlobalRef (o, getEnv()) {}
WeakGlobalRef (jobject o, JNIEnv* env) : obj (retain (o, env)) {}
WeakGlobalRef (const WeakGlobalRef& o) : obj (retain (o.obj)) {}
WeakGlobalRef (WeakGlobalRef&& o) noexcept : obj (std::exchange (o.obj, nullptr)) {}
WeakGlobalRef& operator= (const WeakGlobalRef& o)
{
WeakGlobalRef { o }.swap (*this);
return *this;
}
WeakGlobalRef& operator= (WeakGlobalRef&& o) noexcept
{
WeakGlobalRef { std::move (o) }.swap (*this);
return *this;
}
~WeakGlobalRef() noexcept { clear(); }
void clear (JNIEnv* env = getEnv())
{
if (obj != nullptr)
env->DeleteWeakGlobalRef (obj);
}
auto lock (JNIEnv* env = getEnv()) const
{
return LocalRef { env->NewLocalRef (obj) };
}
private:
void swap (WeakGlobalRef& other) noexcept
{
std::swap (other.obj, obj);
}
static jweak retain (jweak o, JNIEnv* env = getEnv())
{
return env->NewWeakGlobalRef (o);
}
jweak obj = nullptr;
};
//==============================================================================
class JuceActivityWatcher final : public ActivityLifecycleCallbacks
{
@ -122,21 +172,16 @@ public:
ScopedLock lock (currentActivityLock);
if (currentActivity != nullptr)
if (auto locked = currentActivity.lock (env))
{
// see Clarification June 2001 in JNI reference for why this is
// necessary
LocalRef<jobject> localStorage (env->NewLocalRef (currentActivity));
if (env->IsSameObject (localStorage.get(), activity) != 0)
if (env->IsSameObject (locked, activity) != 0)
return;
env->DeleteWeakGlobalRef (currentActivity);
currentActivity = nullptr;
currentActivity = {};
}
if (activity != nullptr)
currentActivity = env->NewWeakGlobalRef (activity);
currentActivity = WeakGlobalRef { activity };
}
void onActivityStopped (jobject activity) override
@ -145,16 +190,15 @@ public:
ScopedLock lock (currentActivityLock);
if (currentActivity != nullptr)
if (auto locked = currentActivity.lock (env))
{
// important that the comparison happens in this order
// to avoid race condition where the weak reference becomes null
// just after the first check
if (env->IsSameObject (currentActivity, activity) != 0
|| env->IsSameObject (currentActivity, nullptr) != 0)
if (env->IsSameObject (locked, activity) != 0
|| env->IsSameObject (locked, nullptr) != 0)
{
env->DeleteWeakGlobalRef (currentActivity);
currentActivity = nullptr;
currentActivity = {};
}
}
}
@ -162,13 +206,13 @@ public:
LocalRef<jobject> getCurrent()
{
ScopedLock lock (currentActivityLock);
return LocalRef<jobject> (getEnv()->NewLocalRef (currentActivity));
return currentActivity.lock();
}
LocalRef<jobject> getMain()
{
ScopedLock lock (currentActivityLock);
return LocalRef<jobject> (getEnv()->NewLocalRef (mainActivity));
return mainActivity.lock();
}
static JuceActivityWatcher& getInstance()
@ -184,16 +228,13 @@ private:
ScopedLock lock (currentActivityLock);
if (mainActivity != nullptr)
if (auto locked = mainActivity.lock (env))
{
if (env->IsSameObject (mainActivity, nullptr) != 0)
{
env->DeleteWeakGlobalRef (mainActivity);
mainActivity = nullptr;
}
if (env->IsSameObject (locked, nullptr) != 0)
mainActivity = {};
}
if (mainActivity == nullptr)
if (mainActivity.lock() == nullptr)
{
auto appContext = getAppContext();
auto mainActivityPath = getMainActivityClassPath();
@ -206,7 +247,7 @@ private:
// This may be problematic for apps which use several activities with the same type. We just
// assume that the very first activity of this type is the main one
if (activityPath == mainActivityPath)
mainActivity = env->NewWeakGlobalRef (context);
mainActivity = WeakGlobalRef { context };
}
}
}
@ -250,8 +291,7 @@ private:
}
CriticalSection currentActivityLock;
jweak currentActivity = nullptr;
jweak mainActivity = nullptr;
WeakGlobalRef currentActivity, mainActivity;
ActivityLifecycleCallbackForwarder forwarder { GlobalRef { getAppContext() }, this };
};
@ -287,7 +327,7 @@ void Thread::initialiseJUCE (void* jniEnv, void* context)
firstCall = false;
// if we ever support unloading then this should probably be a weak reference
androidApkContext = env->NewGlobalRef (static_cast<jobject> (context));
androidApkContext = GlobalRef { LocalRef { (jobject) context } };
JuceActivityWatcher::getInstance();
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID