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:
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)
|
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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue