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:
parent
8ce1f19bf0
commit
d64b9e7782
3 changed files with 75 additions and 36 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue