diff options
author | Assam Boudjelthia <[email protected]> | 2025-07-06 04:29:15 +0300 |
---|---|---|
committer | Assam Boudjelthia <[email protected]> | 2025-08-08 23:39:21 +0300 |
commit | 6aba83405cea658a5c58eb70dd31f41a9135ae49 (patch) | |
tree | 441ab2e028ce0c1e1611816875fa20f16e25e966 | |
parent | db00051673f22130ba4543e317d91c947acf2571 (diff) |
Android: simplify the Qt app's exit logic
Currently, we have terminateQt(), quitApp(), quitQt() and also
quitQtCoreApplication(). All these calls are called at some point
or another while tearing down the Qt application.
Right after main() returns, we call quitApp() which finish() the
QtActivity and sets isStarted to false, then shortly after onDestroy()
would be called, which calls terminateQt() which does the cleanup in
native C++ side and then exit()/join() the Qt thread.
The other call quitQt(), does mainly the same with the exception that
it won't finish() the Activity because it's a normal Activity and it's
owned by Qt, this is the way in Quick for Android use case.
These many calls makes the exit sequence slightly convoluted, and to
simplify it, we could do it slightly differently. Firstly, right after
main() returns, we set isStarted to false, then for normal Qt apps we
call finish() -> onDestroy() then calls terminateQt(), and for Quick for
Android apps, their delegates would call terminateQt() directly. At the
end terminateQt() would exit()/join() the Qt thread.
To follow the naming of startQtNativeApplication(), this renames
terminateQt() to terminateQtNativeApplication().
Change-Id: Iefe7fcc23039b3c58f81a8a3d7f38097f97ee07c
Reviewed-by: Petri Virkkunen <[email protected]>
8 files changed, 40 insertions, 48 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java index 1e29f9b858e..e068a693804 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java @@ -76,7 +76,9 @@ public class QtActivityBase extends Activity private void restartApplication() { Intent intent = Intent.makeRestartActivityTask(getComponentName()); startActivity(intent); - QtNative.quitApp(); + QtNative.setStarted(false); + // FIXME: calling exit() right after this gives no time to get onDestroy(). + finish(); Runtime.getRuntime().exit(0); } @@ -162,9 +164,8 @@ public class QtActivityBase extends Activity super.onDestroy(); if (!m_retainNonConfigurationInstance) { QtNative.unregisterAppStateListener(m_delegate); - QtNative.terminateQt(); + QtNative.terminateQtNativeApplication(); QtNative.setActivity(null); - QtNative.getQtThread().exit(); System.exit(0); } } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java b/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java index de572266b93..205c658b661 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java @@ -8,8 +8,7 @@ import android.app.Application; public class QtApplicationBase extends Application { @Override public void onTerminate() { - QtNative.terminateQt(); - QtNative.getQtThread().exit(); super.onTerminate(); + QtNative.terminateQtNativeApplication(); } } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java index d6f2ff5959f..b55aa957506 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java @@ -75,7 +75,7 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase m_activity.getApplication().unregisterActivityLifecycleCallbacks(this); QtNative.unregisterAppStateListener(QtEmbeddedDelegate.this); QtEmbeddedViewInterfaceFactory.remove(m_activity); - QtNative.quitQt(); + QtNative.terminateQtNativeApplication(); QtNative.setActivity(null); } } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/QtNative.java index 637e1510df4..135dfe8c47c 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java @@ -204,11 +204,11 @@ public class QtNative } static QtThread getQtThread() { - if (m_qtThread != null) + if (m_qtThread != null && m_qtThread.isAlive()) return m_qtThread; synchronized (m_qtThreadLock) { - if (m_qtThread == null) + if (m_qtThread == null || !m_qtThread.isAlive()) m_qtThread = new QtThread(); return m_qtThread; @@ -365,31 +365,6 @@ public class QtNative getQtThread().post(() -> { startQtNativeApplication(qtParams); }); } - static void quitApp() - { - runAction(() -> { - if (isActivityValid()) - m_activity.get().finish(); - if (isServiceValid()) - m_service.get().stopSelf(); - m_stateDetails.isStarted = false; - notifyAppStateDetailsChanged(m_stateDetails); - }); - } - - static void quitQt() - { - runAction(() -> { - terminateQt(); - m_stateDetails.isStarted = false; - notifyAppStateDetailsChanged(m_stateDetails); - getQtThread().exit(); - synchronized (m_qtThreadLock) { - m_qtThread = null; - } - }); - } - @UsedFromNativeCode static int checkSelfPermission(String permission) { @@ -455,7 +430,7 @@ public class QtNative // application methods static native void startQtNativeApplication(String params); static native void quitQtCoreApplication(); - static native void terminateQt(); + static native void terminateQtNativeApplication(); static native boolean updateNativeActivity(); // application methods diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java index d5d099a1d77..9c504f4b21f 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java @@ -50,9 +50,8 @@ public class QtServiceBase extends Service { { super.onDestroy(); QtNative.quitQtCoreApplication(); - QtNative.terminateQt(); + QtNative.terminateQtNativeApplication(); QtNative.setService(null); - QtNative.getQtThread().exit(); System.exit(0); } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java index 52ca1d92709..54895b02682 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java @@ -87,8 +87,7 @@ class QtServiceEmbeddedDelegate implements QtEmbeddedViewInterface, QtNative.App QtNative.unregisterAppStateListener(QtServiceEmbeddedDelegate.this); QtEmbeddedViewInterfaceFactory.remove(m_service); - QtNative.terminateQt(); + QtNative.terminateQtNativeApplication(); QtNative.setService(null); - QtNative.getQtThread().exit(); } } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtThread.java b/src/android/jar/src/org/qtproject/qt/android/QtThread.java index e7f1269d110..51b10ff9c5f 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtThread.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtThread.java @@ -78,4 +78,9 @@ class QtThread { e.printStackTrace(); } } + + boolean isAlive() + { + return m_qtThread.isAlive(); + } } diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 621d8f5aa01..101654627e4 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -72,7 +72,7 @@ static jmethodID m_bitmapDrawableConstructorMethodID = nullptr; extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application static Main m_main = nullptr; -static sem_t m_exitSemaphore, m_terminateSemaphore; +static sem_t m_exitSemaphore, m_stopQtSemaphore; static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr; @@ -94,6 +94,8 @@ Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_I Q_DECLARE_JNI_CLASS(QtAccessibilityInterface, "org/qtproject/qt/android/QtAccessibilityInterface"); #endif +Q_DECLARE_JNI_CLASS(QtThread, "org/qtproject/qt/android/QtThread"); + namespace QtAndroid { QBasicMutex *platformInterfaceMutex() @@ -365,7 +367,7 @@ static void initializeBackends() static bool initCleanupHandshakeSemaphores() { return sem_init(&m_exitSemaphore, 0, 0) != -1 - && sem_init(&m_terminateSemaphore, 0, 0) != -1; + && sem_init(&m_stopQtSemaphore, 0, 0) != -1; } static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring paramsString) @@ -461,18 +463,27 @@ static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring param const int ret = m_main(argc, argv.data()); qInfo() << "main() returned" << ret; + QtNative::callStaticMethod("setStarted", false); + if (mainLibraryHnd) { int res = dlclose(mainLibraryHnd); if (res < 0) qWarning() << "dlclose failed:" << dlerror(); } - if (m_applicationClass) { - const auto quitMethodName = QtAndroid::isQtApplication() ? "quitApp" : "quitQt"; - QJniObject::callStaticMethod<void>(m_applicationClass, quitMethodName); + if (QtAndroid::isQtApplication()) { + // Now, that the Qt application has exited, tear down the Activity and Service + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([]() { + auto activity = QtAndroidPrivate::activity(); + if (activity.isValid()) + activity.callMethod("finish"); + auto service = QtAndroidPrivate::service(); + if (service.isValid()) + service.callMethod("stopSelf()"); + }); } - sem_post(&m_terminateSemaphore); + sem_post(&m_stopQtSemaphore); sem_wait(&m_exitSemaphore); sem_destroy(&m_exitSemaphore); @@ -527,7 +538,7 @@ static void clearJavaReferences(JNIEnv *env) } } -static void terminateQt(JNIEnv *env, jclass /*clazz*/) +static void terminateQtNativeApplication(JNIEnv *env, jclass /*clazz*/) { // QAndroidEventDispatcherStopper is stopped when the user uses the task manager to kill the application if (QAndroidEventDispatcherStopper::instance()->stopped()) { @@ -537,9 +548,9 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/) } if (startQtAndroidPluginCalled.loadAcquire()) - sem_wait(&m_terminateSemaphore); + sem_wait(&m_stopQtSemaphore); - sem_destroy(&m_terminateSemaphore); + sem_destroy(&m_stopQtSemaphore); clearJavaReferences(env); @@ -553,6 +564,9 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/) delete m_backendRegister; m_backendRegister = nullptr; sem_post(&m_exitSemaphore); + + // Terminate the QtThread + QtNative::callStaticMethod<QtThread>("getQtThread").callMethod("exit"); } static void handleLayoutSizeChanged(JNIEnv * /*env*/, jclass /*clazz*/, @@ -714,7 +728,7 @@ static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent) static JNINativeMethod methods[] = { { "startQtNativeApplication", "(Ljava/lang/String;)V", (void *)startQtNativeApplication }, { "quitQtCoreApplication", "()V", (void *)quitQtCoreApplication }, - { "terminateQt", "()V", (void *)terminateQt }, + { "terminateQtNativeApplication", "()V", (void *)terminateQtNativeApplication }, { "updateApplicationState", "(I)V", (void *)updateApplicationState }, { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult }, { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent }, |