summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAssam Boudjelthia <[email protected]>2025-07-06 04:29:15 +0300
committerAssam Boudjelthia <[email protected]>2025-08-08 23:39:21 +0300
commit6aba83405cea658a5c58eb70dd31f41a9135ae49 (patch)
tree441ab2e028ce0c1e1611816875fa20f16e25e966
parentdb00051673f22130ba4543e317d91c947acf2571 (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]>
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java7
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java3
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java2
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtNative.java31
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java3
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java3
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtThread.java5
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp34
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 },