1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +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) 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; SystemJavaClassComparator comparator;
@ -375,7 +375,7 @@ void JNIClassBase::initialise (JNIEnv* env, jobject context)
} }
if (classRef == nullptr) if (classRef == nullptr)
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath))); classRef = GlobalRefImpl { LocalRef { env->FindClass (classPath) } };
jassert (classRef != nullptr); jassert (classRef != nullptr);
initialiseFields (env); 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 // 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... // 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()) if (jthrowable exception = env->ExceptionOccurred())
{ {
env->ExceptionClear(); env->ExceptionClear();
classObj = nullptr; classObj = {};
} }
// later versions of Android don't throw at all, so re-check the object // later versions of Android don't throw at all, so re-check the object
if (classObj != nullptr) if (classObj != nullptr)
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (classObj)); classRef = GlobalRefImpl { classObj };
} }
void JNIClassBase::release (JNIEnv* env) void JNIClassBase::release (JNIEnv* env)
{ {
if (classRef != nullptr) classRef.clear (env);
env->DeleteGlobalRef (classRef);
} }
void JNIClassBase::initialiseAllClasses (JNIEnv* env, jobject context) void JNIClassBase::initialiseAllClasses (JNIEnv* env, jobject context)

View file

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

View file

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