summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBogDan Vatra <bogdan@kdab.com>2018-02-02 15:20:51 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2018-02-11 12:35:29 +0000
commit2c9dcfa00468c349375b63b92c55d161eee2289d (patch)
treed82ebb909cebe7ad528a91411c455ec20f9fe5fc
parent9b800b55c1c4f2b3e6ed365736e357eb31423c95 (diff)
Load Qt libs from Qt thread
[ChangeLog][Android] The application and dependent Qt libraries are now loaded on the same thread as main() is run on, ensuring that global static initializers, constructor functions, and main() are all run on the same thread. The same applies during application shutdown, for destructors of global objects, and destructor functions. Change-Id: Id4bfece1ed2a0532ed2e8fb7d8ffd6e55d5a10dc Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
-rw-r--r--src/android/jar/jar.pro3
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java4
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtNative.java134
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java4
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtThread.java112
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java4
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp107
7 files changed, 255 insertions, 113 deletions
diff --git a/src/android/jar/jar.pro b/src/android/jar/jar.pro
index 603e28aeee..683866a345 100644
--- a/src/android/jar/jar.pro
+++ b/src/android/jar/jar.pro
@@ -21,7 +21,8 @@ JAVASOURCES += \
$$PATHPREFIX/ExtractStyle.java \
$$PATHPREFIX/EditMenu.java \
$$PATHPREFIX/EditPopupMenu.java \
- $$PATHPREFIX/CursorHandle.java
+ $$PATHPREFIX/CursorHandle.java \
+ $$PATHPREFIX/QtThread.java
# install
target.path = $$[QT_INSTALL_PREFIX]/jar
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java
index 0e28b964e8..6b8577116e 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java
@@ -921,6 +921,7 @@ public class QtActivityDelegate
public void onTerminate()
{
QtNative.terminateQt();
+ QtNative.m_qtThread.exit();
}
public void onCreate(Bundle savedInstanceState)
@@ -1076,7 +1077,8 @@ public class QtActivityDelegate
QtNative.setActivity(null, null);
if (m_debuggerProcess != null)
m_debuggerProcess.destroy();
- System.exit(0);// FIXME remove it or find a better way
+ QtNative.m_qtThread.exit();
+ System.exit(0);
}
}
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
index 1cf3bca5f7..3db3453263 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
@@ -98,6 +98,7 @@ public class QtNative
private static ClipboardManager m_clipboardManager = null;
private static Method m_checkSelfPermissionMethod = null;
private static Boolean m_tabletEventSupported = null;
+ public static QtThread m_qtThread = new QtThread();
private static final Runnable runPendingCppRunnablesRunnable = new Runnable() {
@Override
public void run() {
@@ -164,55 +165,64 @@ public class QtNative
}
// this method loads full path libs
- public static void loadQtLibraries(ArrayList<String> libraries)
+ public static void loadQtLibraries(final ArrayList<String> libraries)
{
- if (libraries == null)
- return;
-
- for (String libName : libraries) {
- try {
- File f = new File(libName);
- if (f.exists())
- System.load(libName);
- } catch (SecurityException e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
- } catch (Exception e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
+ m_qtThread.run(new Runnable() {
+ @Override
+ public void run() {
+ if (libraries == null)
+ return;
+ for (String libName : libraries) {
+ try {
+ File f = new File(libName);
+ if (f.exists())
+ System.load(libName);
+ } catch (SecurityException e) {
+ Log.i(QtTAG, "Can't load '" + libName + "'", e);
+ } catch (Exception e) {
+ Log.i(QtTAG, "Can't load '" + libName + "'", e);
+ }
+ }
}
- }
+ });
}
// this method loads bundled libs by name.
- public static void loadBundledLibraries(ArrayList<String> libraries, String nativeLibraryDir)
+ public static void loadBundledLibraries(final ArrayList<String> libraries, final String nativeLibraryDir)
{
- if (libraries == null)
- return;
+ m_qtThread.run(new Runnable() {
+ @Override
+ public void run() {
+ if (libraries == null)
+ return;
- for (String libName : libraries) {
- try {
- String libNameTemplate = "lib" + libName + ".so";
- File f = new File(nativeLibraryDir + libNameTemplate);
- if (!f.exists()) {
- Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
+ for (String libName : libraries) {
try {
- ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(),
- PackageManager.GET_META_DATA);
- String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
- if (info.metaData.containsKey("android.app.system_libs_prefix"))
- systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
- f = new File(systemLibraryDir + libNameTemplate);
- } catch (Exception e) {
+ String libNameTemplate = "lib" + libName + ".so";
+ File f = new File(nativeLibraryDir + libNameTemplate);
+ if (!f.exists()) {
+ Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
+ try {
+ ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(),
+ PackageManager.GET_META_DATA);
+ String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
+ if (info.metaData.containsKey("android.app.system_libs_prefix"))
+ systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
+ f = new File(systemLibraryDir + libNameTemplate);
+ } catch (Exception e) {
+ }
+ }
+ if (f.exists())
+ System.load(f.getAbsolutePath());
+ else
+ Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
+ } catch (Exception e) {
+ Log.i(QtTAG, "Can't load '" + libName + "'", e);
}
}
- if (f.exists())
- System.load(f.getAbsolutePath());
- else
- Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
- } catch (Exception e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
}
- }
+ });
}
public static void setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate)
@@ -293,7 +303,7 @@ public class QtNative
}
public static boolean startApplication(String params,
- String environment,
+ final String environment,
String mainLibrary,
String nativeLibraryDir) throws Exception
{
@@ -317,23 +327,42 @@ public class QtNative
if (params == null)
params = "-platform\tandroid";
- boolean res = false;
+ final String mainLibraryPath = f.getAbsolutePath();
+ final boolean[] res = new boolean[1];
+ res[0] = false;
synchronized (m_mainActivityMutex) {
- res = startQtAndroidPlugin();
- setDisplayMetrics(m_displayMetricsScreenWidthPixels,
- m_displayMetricsScreenHeightPixels,
- m_displayMetricsDesktopWidthPixels,
- m_displayMetricsDesktopHeightPixels,
- m_displayMetricsXDpi,
- m_displayMetricsYDpi,
- m_displayMetricsScaledDensity,
- m_displayMetricsDensity);
if (params.length() > 0 && !params.startsWith("\t"))
params = "\t" + params;
- startQtApplication(f.getAbsolutePath() + params, environment);
+ final String qtParams = f.getAbsolutePath() + params;
+ m_qtThread.run(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ System.load(mainLibraryPath);
+ } catch (Exception e) {
+ Log.i(QtTAG, "Can't load '" + mainLibraryPath + "'", e);
+ }
+ res[0] = startQtAndroidPlugin(qtParams, environment);
+ setDisplayMetrics(m_displayMetricsScreenWidthPixels,
+ m_displayMetricsScreenHeightPixels,
+ m_displayMetricsDesktopWidthPixels,
+ m_displayMetricsDesktopHeightPixels,
+ m_displayMetricsXDpi,
+ m_displayMetricsYDpi,
+ m_displayMetricsScaledDensity,
+ m_displayMetricsDensity);
+ }
+ });
+ m_qtThread.post(new Runnable() {
+ @Override
+ public void run() {
+ startQtApplication();
+ }
+ });
+ waitForServiceSetup();
m_started = true;
}
- return res;
+ return res[0];
}
public static void setApplicationDisplayMetrics(int screenWidthPixels,
@@ -377,8 +406,9 @@ public class QtNative
// application methods
- public static native void startQtApplication(String params, String env);
- public static native boolean startQtAndroidPlugin();
+ public static native boolean startQtAndroidPlugin(String params, String env);
+ public static native void startQtApplication();
+ public static native void waitForServiceSetup();
public static native void quitQtCoreApplication();
public static native void quitQtAndroidPlugin();
public static native void terminateQt();
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java
index 195ec376c9..ae06fa6268 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java
@@ -187,6 +187,10 @@ public class QtServiceDelegate
public void onDestroy()
{
QtNative.quitQtCoreApplication();
+ QtNative.terminateQt();
+ QtNative.setService(null, null);
+ QtNative.m_qtThread.exit();
+ System.exit(0);
}
public IBinder onBind(Intent intent)
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtThread.java b/src/android/jar/src/org/qtproject/qt5/android/QtThread.java
new file mode 100644
index 0000000000..975e787345
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtThread.java
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 BogDan Vatra <bogdan@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Android port of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+package org.qtproject.qt5.android;
+
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+public class QtThread {
+ private ArrayList<Runnable> m_pendingRunnables = new ArrayList<Runnable>();
+ private boolean m_exit = false;
+ private Thread m_qtThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (!m_exit) {
+ try {
+ ArrayList<Runnable> pendingRunnables;
+ synchronized (m_qtThread) {
+ if (m_pendingRunnables.size() == 0)
+ m_qtThread.wait();
+ pendingRunnables = new ArrayList<Runnable>(m_pendingRunnables);
+ m_pendingRunnables.clear();
+ }
+ for (Runnable runnable : pendingRunnables)
+ runnable.run();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ });
+
+ QtThread() {
+ m_qtThread.setName("qtMainLoopThread");
+ m_qtThread.start();
+ }
+
+ public void post(final Runnable runnable) {
+ synchronized (m_qtThread) {
+ m_pendingRunnables.add(runnable);
+ m_qtThread.notify();
+ }
+ }
+
+ public void run(final Runnable runnable) {
+ final Semaphore sem = new Semaphore(0);
+ synchronized (m_qtThread) {
+ m_pendingRunnables.add(new Runnable() {
+ @Override
+ public void run() {
+ runnable.run();
+ sem.release();
+ }
+ });
+ m_qtThread.notify();
+ }
+ try {
+ sem.acquire();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void exit()
+ {
+ m_exit = true;
+ synchronized (m_qtThread) {
+ m_qtThread.notify();
+ }
+ try {
+ m_qtThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java
index 6e92e64028..fb6f61e31e 100644
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java
+++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java
@@ -243,10 +243,6 @@ public abstract class QtLoader {
QtApplication.setQtContextDelegate(m_delegateClass, qtLoader);
- // now load the application library so it's accessible from this class loader
- if (libName != null)
- System.loadLibrary(libName);
-
Method startAppMethod=qtLoader.getClass().getMethod("startApplication");
if (!(Boolean)startAppMethod.invoke(qtLoader))
throw new Exception("");
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 2bdd49dc50..13d41bea99 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -59,6 +59,7 @@
#include "qandroideventdispatcher.h"
#include <android/api-level.h>
+#include <QtCore/qthread.h>
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/private/qjni_p.h>
#include <QtGui/private/qguiapplication_p.h>
@@ -99,7 +100,6 @@ extern "C" typedef int (*Main)(int, char **); //use the standard main method to
static Main m_main = nullptr;
static void *m_mainLibraryHnd = nullptr;
static QList<QByteArray> m_applicationParams;
-pthread_t m_qtAppThread = 0;
static sem_t m_exitSemaphore, m_terminateSemaphore;
QHash<int, AndroidSurfaceClient *> m_surfaces;
@@ -441,57 +441,10 @@ namespace QtAndroid
} // namespace QtAndroid
-static jboolean startQtAndroidPlugin(JNIEnv* /*env*/, jobject /*object*//*, jobject applicationAssetManager*/)
+static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
{
m_androidPlatformIntegration = nullptr;
m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
- return true;
-}
-
-static void *startMainMethod(void */*data*/)
-{
- {
- JNIEnv* env = nullptr;
- JavaVMAttachArgs args;
- args.version = JNI_VERSION_1_6;
- args.name = "QtMainThread";
- args.group = NULL;
- JavaVM *vm = QtAndroidPrivate::javaVM();
- if (vm != 0)
- vm->AttachCurrentThread(&env, &args);
- }
-
- QVarLengthArray<const char *> params(m_applicationParams.size());
- for (int i = 0; i < m_applicationParams.size(); i++)
- params[i] = static_cast<const char *>(m_applicationParams[i].constData());
-
- int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
-
- if (m_mainLibraryHnd) {
- int res = dlclose(m_mainLibraryHnd);
- if (res < 0)
- qWarning() << "dlclose failed:" << dlerror();
- }
-
- if (m_applicationClass)
- QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
-
- // All attached threads should be detached before returning from this function.
- JavaVM *vm = QtAndroidPrivate::javaVM();
- if (vm != 0)
- vm->DetachCurrentThread();
-
- sem_post(&m_terminateSemaphore);
- sem_wait(&m_exitSemaphore);
- sem_destroy(&m_exitSemaphore);
-
- // We must call exit() to ensure that all global objects will be destructed
- exit(ret);
- return 0;
-}
-
-static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
-{
m_mainLibraryHnd = nullptr;
{ // Set env. vars
const char *nativeString = env->GetStringUTFChars(environmentString, 0);
@@ -540,14 +493,54 @@ static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring para
if (sem_init(&m_terminateSemaphore, 0, 0) == -1)
return false;
- jboolean res = pthread_create(&m_qtAppThread, nullptr, startMainMethod, nullptr) == 0;
+ return true;
+}
+static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
+{
+ Q_UNUSED(env);
// The service must wait until the QCoreApplication starts otherwise onBind will be
// called too early
if (m_serviceObject)
QtAndroidPrivate::waitForServiceSetup();
+}
+
+static jboolean startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
+{
+ {
+ JNIEnv* env = nullptr;
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_6;
+ args.name = "QtMainThread";
+ args.group = NULL;
+ JavaVM *vm = QtAndroidPrivate::javaVM();
+ if (vm != 0)
+ vm->AttachCurrentThread(&env, &args);
+ }
+
+ QVarLengthArray<const char *> params(m_applicationParams.size());
+ for (int i = 0; i < m_applicationParams.size(); i++)
+ params[i] = static_cast<const char *>(m_applicationParams[i].constData());
+
+ int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
+
+ if (m_mainLibraryHnd) {
+ int res = dlclose(m_mainLibraryHnd);
+ if (res < 0)
+ qWarning() << "dlclose failed:" << dlerror();
+ }
- return res;
+ if (m_applicationClass) {
+ qWarning("exit app 0");
+ QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
+ }
+
+ sem_post(&m_terminateSemaphore);
+ sem_wait(&m_exitSemaphore);
+ sem_destroy(&m_exitSemaphore);
+
+ // We must call exit() to ensure that all global objects will be destructed
+ exit(ret);
}
static void quitQtCoreApplication(JNIEnv *env, jclass /*clazz*/)
@@ -593,7 +586,6 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
if (!QAndroidEventDispatcherStopper::instance()->stopped()) {
sem_post(&m_exitSemaphore);
- pthread_join(m_qtAppThread, nullptr);
}
}
@@ -758,11 +750,12 @@ static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
}
static JNINativeMethod methods[] = {
- {"startQtAndroidPlugin", "()Z", (void *)startQtAndroidPlugin},
- {"startQtApplication", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)startQtApplication},
+ {"startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)startQtAndroidPlugin},
+ {"startQtApplication", "()V", (void *)startQtApplication},
{"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin},
{"quitQtCoreApplication", "()V", (void *)quitQtCoreApplication},
{"terminateQt", "()V", (void *)terminateQt},
+ {"waitForServiceSetup", "()V", (void *)waitForServiceSetup},
{"setDisplayMetrics", "(IIIIDDDD)V", (void *)setDisplayMetrics},
{"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface},
{"updateWindow", "()V", (void *)updateWindow},
@@ -884,7 +877,6 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
void *venv;
} UnionJNIEnvToVoid;
- __android_log_print(ANDROID_LOG_INFO, "Qt", "qt start");
UnionJNIEnvToVoid uenv;
uenv.venv = nullptr;
m_javaVM = nullptr;
@@ -906,5 +898,10 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
m_javaVM = vm;
+ // attach qt main thread data to this thread
+ QObject threadSetter;
+ if (threadSetter.thread())
+ threadSetter.thread()->setObjectName("QtMainLoopThread");
+ __android_log_print(ANDROID_LOG_INFO, "Qt", "qt started");
return JNI_VERSION_1_4;
}