diff options
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; } |