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

Android: Refactor lifecycle callbacks to reduce repetition in lifecycle listener registration

This commit is contained in:
reuk 2025-07-28 12:52:16 +01:00
parent 11752d061f
commit 1c2eb2d056
No known key found for this signature in database
7 changed files with 56 additions and 137 deletions

View file

@ -429,7 +429,22 @@ void juce_dispatchDelete (JNIEnv*, jobject /*object*/, jlong host)
}
//==============================================================================
jobject ActivityLifecycleCallbacks::invoke (jobject proxy, jobject method, jobjectArray args)
ActivityLifecycleCallbackForwarder::ActivityLifecycleCallbackForwarder (GlobalRef ctx, ActivityLifecycleCallbacks* cb)
: appContext (ctx),
myself (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks")),
callbacks (cb)
{
if (appContext != nullptr && myself != nullptr)
getEnv()->CallVoidMethod (appContext, AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
}
ActivityLifecycleCallbackForwarder::~ActivityLifecycleCallbackForwarder()
{
if (appContext != nullptr && myself != nullptr)
getEnv()->CallVoidMethod (appContext, AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
}
jobject ActivityLifecycleCallbackForwarder::invoke (jobject proxy, jobject method, jobjectArray args)
{
auto* env = getEnv();
@ -475,7 +490,7 @@ jobject ActivityLifecycleCallbacks::invoke (jobject proxy, jobject method, jobje
const auto activity = env->GetArrayLength (args) > 0 ? env->GetObjectArrayElement (args, 0) : (jobject) nullptr;
const auto bundle = env->GetArrayLength (args) > 1 ? env->GetObjectArrayElement (args, 1) : (jobject) nullptr;
(iter->second) (*this, activity, bundle);
(iter->second) (*callbacks, activity, bundle);
return nullptr;
}

View file

@ -1016,9 +1016,11 @@ LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName);
//==============================================================================
class ActivityLifecycleCallbacks : public AndroidInterfaceImplementer
class ActivityLifecycleCallbacks
{
public:
virtual ~ActivityLifecycleCallbacks() = default;
virtual void onActivityPreCreated (jobject /*activity*/, jobject /*bundle*/) {}
virtual void onActivityPreDestroyed (jobject /*activity*/) {}
virtual void onActivityPrePaused (jobject /*activity*/) {}
@ -1044,15 +1046,27 @@ public:
virtual void onActivityPostStopped (jobject /*activity*/) {}
virtual void onActivityConfigurationChanged (jobject /*activity*/) {}
};
class ActivityLifecycleCallbackForwarder : private AndroidInterfaceImplementer
{
public:
ActivityLifecycleCallbackForwarder (GlobalRef appContext, ActivityLifecycleCallbacks* callbacks);
~ActivityLifecycleCallbackForwarder() override;
private:
jobject invoke (jobject, jobject, jobjectArray) override;
GlobalRef appContext;
GlobalRef myself;
ActivityLifecycleCallbacks* callbacks = nullptr;
};
//==============================================================================
struct SurfaceHolderCallback : AndroidInterfaceImplementer
struct SurfaceHolderCallback : public AndroidInterfaceImplementer
{
virtual ~SurfaceHolderCallback() override = default;
~SurfaceHolderCallback() override = default;
virtual void surfaceChanged (LocalRef<jobject> holder, int format, int width, int height) = 0;
virtual void surfaceCreated (LocalRef<jobject> holder) = 0;

View file

@ -111,33 +111,9 @@ class JuceActivityWatcher final : public ActivityLifecycleCallbacks
public:
JuceActivityWatcher()
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
}
checkActivityIsMain (androidApkContext);
}
~JuceActivityWatcher() override
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr && myself != nullptr)
{
auto* env = getEnv();
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
clear();
myself.clear();
}
}
void onActivityStarted (jobject activity) override
{
auto* env = getEnv();
@ -273,10 +249,10 @@ private:
return mainActivityClassPath;
}
GlobalRef myself;
CriticalSection currentActivityLock;
jweak currentActivity = nullptr;
jweak mainActivity = nullptr;
ActivityLifecycleCallbackForwarder forwarder { GlobalRef { getAppContext() }, this };
};
//==============================================================================

View file

@ -183,29 +183,6 @@ public:
JuceAppLifecycle (juce::JUCEApplicationBase* (*initSymbolAddr)())
: createApplicationSymbol (initSymbolAddr)
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
}
}
~JuceAppLifecycle() override
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr && myself != nullptr)
{
auto* env = getEnv();
clear();
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
myself.clear();
}
}
void onActivityCreated (jobject, jobject) override
@ -292,6 +269,7 @@ private:
GlobalRef myself;
juce::JUCEApplicationBase* (*createApplicationSymbol)();
bool hasBeenInitialised = false;
ActivityLifecycleCallbackForwarder forwarder { GlobalRef { getAppContext() }, this };
};
//==============================================================================

View file

@ -1308,7 +1308,8 @@ static constexpr int translateAndroidKeyboardFlags (int javaFlags) noexcept
}
//==============================================================================
class AndroidComponentPeer final : public ComponentPeer
class AndroidComponentPeer final : public ComponentPeer,
private ActivityLifecycleCallbacks
{
public:
AndroidComponentPeer (Component& comp, int windowStyleFlags, void* nativeViewHandle)
@ -2097,20 +2098,12 @@ public:
{
void onActivityStarted (jobject /*activity*/) override
{
auto* env = getEnv();
LocalRef<jobject> appContext (getAppContext());
forceDisplayUpdate();
if (appContext.get() != nullptr)
{
env->CallVoidMethod (appContext.get(),
AndroidApplication.unregisterActivityLifecycleCallbacks,
activityCallbackListener.get());
clear();
activityCallbackListener.clear();
forceDisplayUpdate();
}
AndroidComponentPeer::startupActivityCallbackListener.reset();
}
ActivityLifecycleCallbackForwarder forwarder { GlobalRef { getAppContext() }, this };
};
class MainActivityWindowLayoutListener : public AndroidInterfaceImplementer
@ -2530,7 +2523,7 @@ private:
//==============================================================================
friend class Displays;
inline static AndroidComponentPeer* frontWindow = nullptr;
inline static GlobalRef activityCallbackListener;
inline static std::optional<StartupActivityCallbackListener> startupActivityCallbackListener;
static constexpr jint GRAVITY_LEFT = 0x3, GRAVITY_TOP = 0x30;
static constexpr jint TYPE_APPLICATION = 0x2;
@ -2562,37 +2555,10 @@ bool Desktop::canUseSemiTransparentWindows() noexcept
return true;
}
class Desktop::NativeDarkModeChangeDetectorImpl : public ActivityLifecycleCallbacks
class Desktop::NativeDarkModeChangeDetectorImpl : private ActivityLifecycleCallbacks
{
public:
NativeDarkModeChangeDetectorImpl()
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
}
}
~NativeDarkModeChangeDetectorImpl() override
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr && myself != nullptr)
{
auto* env = getEnv();
env->CallVoidMethod (appContext.get(),
AndroidApplication.unregisterActivityLifecycleCallbacks,
myself.get());
clear();
myself.clear();
}
}
NativeDarkModeChangeDetectorImpl() = default;
bool isDarkModeEnabled() const noexcept { return darkModeEnabled; }
@ -2625,8 +2591,8 @@ private:
UI_MODE_NIGHT_UNDEFINED = 0x00000000,
UI_MODE_NIGHT_YES = 0x00000020;
GlobalRef myself;
bool darkModeEnabled = getDarkModeSetting();
ActivityLifecycleCallbackForwarder forwarder { GlobalRef { getAppContext() }, this };
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
@ -2972,20 +2938,9 @@ void Displays::findDisplays (float masterScale)
}
}
}
else if (AndroidComponentPeer::activityCallbackListener == nullptr)
else if (! AndroidComponentPeer::startupActivityCallbackListener.has_value())
{
LocalRef<jobject> appContext (getAppContext());
if (appContext.get() != nullptr)
{
AndroidComponentPeer::activityCallbackListener = GlobalRef (CreateJavaInterface (
new AndroidComponentPeer::StartupActivityCallbackListener,
"android/app/Application$ActivityLifecycleCallbacks"));
env->CallVoidMethod (appContext,
AndroidApplication.registerActivityLifecycleCallbacks,
AndroidComponentPeer::activityCallbackListener.get());
}
AndroidComponentPeer::startupActivityCallbackListener.emplace();
}
displays.add (d);

View file

@ -490,8 +490,7 @@ private:
};
//==============================================================================
struct CameraDevice::Pimpl
: private ActivityLifecycleCallbacks
struct CameraDevice::Pimpl : private ActivityLifecycleCallbacks
{
using InternalOpenCameraResultCallback = std::function<void (const String& /*cameraId*/, const String& /*error*/)>;
@ -504,7 +503,6 @@ struct CameraDevice::Pimpl
maxWidth (maxWidthToUse),
maxHeight (maxHeightToUse),
cameraId (cameraIdToUse),
activityLifeListener (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks")),
cameraManager (initialiseCameraManager()),
cameraCharacteristics (initialiseCameraCharacteristics (cameraManager, cameraId)),
streamConfigurationMap (cameraCharacteristics),
@ -516,10 +514,7 @@ struct CameraDevice::Pimpl
~Pimpl() override
{
auto* env = getEnv();
env->CallVoidMethod (getAppContext().get(), AndroidApplication.unregisterActivityLifecycleCallbacks, activityLifeListener.get());
activityLifeListener.clear();
callbackForwarder.reset();
}
JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl)
@ -551,7 +546,7 @@ struct CameraDevice::Pimpl
{
if (granted)
{
getEnv()->CallVoidMethod (getAppContext().get(), AndroidApplication.registerActivityLifecycleCallbacks, activityLifeListener.get());
callbackForwarder.emplace (GlobalRef { getAppContext() }, static_cast<ActivityLifecycleCallbacks*> (this));
scopedCameraDevice.reset (new ScopedCameraDevice (*this, cameraId, cameraManager, handler, getAutoFocusModeToUse()));
}
else
@ -2809,7 +2804,7 @@ private:
String cameraId;
InternalOpenCameraResultCallback cameraOpenCallback;
GlobalRef activityLifeListener;
std::optional<ActivityLifecycleCallbackForwarder> callbackForwarder;
GlobalRef cameraManager;
GlobalRef cameraCharacteristics;

View file

@ -344,8 +344,9 @@ private:
};
//==============================================================================
struct VideoComponent::Pimpl
: public AndroidViewComponent, private ActivityLifecycleCallbacks, private SurfaceHolderCallback
struct VideoComponent::Pimpl : public AndroidViewComponent,
private ActivityLifecycleCallbacks,
private SurfaceHolderCallback
{
Pimpl (VideoComponent& ownerToUse, bool)
: owner (ownerToUse),
@ -360,14 +361,6 @@ struct VideoComponent::Pimpl
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
ActivityLifecycleCallbacks* callbacks = dynamic_cast<ActivityLifecycleCallbacks*> (this);
activityLifeListener = GlobalRef (CreateJavaInterface (callbacks, "android/app/Application$ActivityLifecycleCallbacks"));
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, activityLifeListener.get());
}
{
LocalRef<jobject> surfaceView (env->NewObject (AndroidSurfaceView, AndroidSurfaceView.constructor, getAppContext().get()));
LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), AndroidSurfaceView.getHolder));
@ -397,13 +390,6 @@ struct VideoComponent::Pimpl
surfaceHolderCallback.clear();
}
}
if (activityLifeListener != nullptr)
{
env->CallVoidMethod (getAppContext().get(), AndroidApplication.unregisterActivityLifecycleCallbacks, activityLifeListener.get());
ActivityLifecycleCallbacks::clear();
activityLifeListener.clear();
}
}
void loadAsync (const URL& url, std::function<void (const URL&, Result)> callback)
@ -1696,7 +1682,7 @@ private:
VideoComponent& owner;
MediaSession mediaSession;
GlobalRef activityLifeListener;
ActivityLifecycleCallbackForwarder forwarder { GlobalRef { getAppContext() }, this };
#if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
SystemVolumeListener systemVolumeListener;
#endif