From 734c1268269a71b16829d36179e4a090f76c785e Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Mon, 28 Jul 2014 10:41:44 +0300 Subject: Android: Really suspend apps that are put in the background The main event loop will be paused when an application is suspended, this is also the normal behavior of any Android application. When an application is suspended on Android all its Gl surfaces are destroyed and can't be used to render anymore. So, we need to pause the main event loop in order to pause all the timers which might trigger drawings. The event loop is resumed immediately after the application is foreground. AndroidManifest.xml contains more info about how to disable this behavior and what might happen if you do it. [ChangeLog][Android][Important Behavior Changes] The main event loop is now stopped when the app is suspended Task-number: QTBUG-36274 Change-Id: I4c0ba5df9d95f348bca67ea5c76865d6d20775e4 Reviewed-by: Thiago Macieira Reviewed-by: Paul Olav Tvete --- src/android/java/AndroidManifest.xml | 8 ++ .../qtproject/qt5/android/bindings/QtActivity.java | 7 + src/corelib/kernel/qeventdispatcher_unix_p.h | 2 +- src/plugins/platforms/android/android.pro | 6 +- src/plugins/platforms/android/androidjnimain.cpp | 23 +++- src/plugins/platforms/android/androidjnimain.h | 1 + .../platforms/android/qandroideventdispatcher.cpp | 149 +++++++++++++++++++++ .../platforms/android/qandroideventdispatcher.h | 89 ++++++++++++ .../android/qandroidplatformintegration.cpp | 5 +- 9 files changed, 282 insertions(+), 8 deletions(-) create mode 100644 src/plugins/platforms/android/qandroideventdispatcher.cpp create mode 100644 src/plugins/platforms/android/qandroideventdispatcher.h (limited to 'src') diff --git a/src/android/java/AndroidManifest.xml b/src/android/java/AndroidManifest.xml index 8e551ba7ac..e5060f0de9 100644 --- a/src/android/java/AndroidManifest.xml +++ b/src/android/java/AndroidManifest.xml @@ -36,6 +36,14 @@ --> + + + + + diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java index 13d6359d36..c70f1d1aaf 100644 --- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java @@ -892,6 +892,13 @@ public class QtActivity extends Activity getWindow().setBackgroundDrawableResource(m_activityInfo.metaData.getInt("android.app.splash_screen_drawable")); else getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000)); + + if (m_activityInfo.metaData.containsKey("android.app.background_running") + && m_activityInfo.metaData.getBoolean("android.app.background_running")) { + ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=0\t"; + } else { + ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=1\t"; + } startApp(true); } } diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h index 242aa9e695..6060f34b47 100644 --- a/src/corelib/kernel/qeventdispatcher_unix_p.h +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -136,7 +136,7 @@ protected: virtual int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - timespec *timeout) Q_DECL_FINAL; + timespec *timeout); }; class Q_CORE_EXPORT QEventDispatcherUNIXPrivate : public QAbstractEventDispatcherPrivate diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro index 32c37ab17a..0209379afb 100644 --- a/src/plugins/platforms/android/android.pro +++ b/src/plugins/platforms/android/android.pro @@ -51,7 +51,8 @@ SOURCES += $$PWD/androidplatformplugin.cpp \ $$PWD/qandroidplatformbackingstore.cpp \ $$PWD/qandroidplatformopenglcontext.cpp \ $$PWD/qandroidplatformforeignwindow.cpp \ - $$PWD/extract.cpp + $$PWD/extract.cpp \ + $$PWD/qandroideventdispatcher.cpp HEADERS += $$PWD/qandroidplatformintegration.h \ $$PWD/androidjnimain.h \ @@ -78,7 +79,8 @@ HEADERS += $$PWD/qandroidplatformintegration.h \ $$PWD/qandroidplatformrasterwindow.h \ $$PWD/qandroidplatformbackingstore.h \ $$PWD/qandroidplatformopenglcontext.h \ - $$PWD/qandroidplatformforeignwindow.h + $$PWD/qandroidplatformforeignwindow.h \ + $$PWD/qandroideventdispatcher.h #Non-standard install directory, QTBUG-29859 DESTDIR = $$DESTDIR/android diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 3e3e169df9..4ee32d79c2 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -69,6 +69,7 @@ #include #include #include "qandroidassetsfileenginehandler.h" +#include "qandroideventdispatcher.h" #include #include @@ -426,6 +427,12 @@ namespace QtAndroid surfaceId); } + bool blockEventLoopsWhenSuspended() + { + static bool block = qgetenv("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED").toInt(); + return block; + } + } // namespace QtAndroid @@ -596,10 +603,22 @@ static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state { m_activityActive = (state == Qt::ApplicationActive); - if (!m_androidPlatformIntegration || !QGuiApplicationPrivate::platformIntegration()) + if (!m_main || !m_androidPlatformIntegration || !QGuiApplicationPrivate::platformIntegration()) return; - QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state)); + if (state <= Qt::ApplicationInactive) { + // Don't send timers and sockets events anymore if we are going to hide all windows + QAndroidEventDispatcherStopper::instance()->goingToStop(true); + QCoreApplication::processEvents(); + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state)); + QWindowSystemInterface::flushWindowSystemEvents(); + if (state == Qt::ApplicationSuspended) + QAndroidEventDispatcherStopper::instance()->stopAll(); + } else { + QAndroidEventDispatcherStopper::instance()->startAll(); + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state)); + QAndroidEventDispatcherStopper::instance()->goingToStop(false); + } } static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newRotation, jint nativeOrientation) diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h index 29896529ca..4fc2bf1992 100644 --- a/src/plugins/platforms/android/androidjnimain.h +++ b/src/plugins/platforms/android/androidjnimain.h @@ -120,6 +120,7 @@ namespace QtAndroid const char *qtTagText(); QString deviceName(); + bool blockEventLoopsWhenSuspended(); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroideventdispatcher.cpp b/src/plugins/platforms/android/qandroideventdispatcher.cpp new file mode 100644 index 0000000000..074ba71f80 --- /dev/null +++ b/src/plugins/platforms/android/qandroideventdispatcher.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2014 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroideventdispatcher.h" +#include "androidjnimain.h" + +QAndroidEventDispatcher::QAndroidEventDispatcher(QObject *parent) : + QUnixEventDispatcherQPA(parent) +{ + if (QtAndroid::blockEventLoopsWhenSuspended()) + QAndroidEventDispatcherStopper::instance()->addEventDispatcher(this); +} + +QAndroidEventDispatcher::~QAndroidEventDispatcher() +{ + if (QtAndroid::blockEventLoopsWhenSuspended()) + QAndroidEventDispatcherStopper::instance()->removeEventDispatcher(this); +} + +void QAndroidEventDispatcher::start() +{ + if (m_stopRequest.testAndSetAcquire(1, 0)) { + m_dispatcherSemaphore.release(); + wakeUp(); + } +} + +void QAndroidEventDispatcher::stop() +{ + if (m_stopRequest.testAndSetAcquire(0, 1)) { + wakeUp(); + m_stopperSemaphore.acquire(); + } +} + +void QAndroidEventDispatcher::goingToStop(bool stop) +{ + m_goingToStop.store(stop ? 1 : 0); + if (!stop) + wakeUp(); +} + +int QAndroidEventDispatcher::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, timespec *timeout) +{ + if (m_stopRequest.load() == 1) { + m_stopperSemaphore.release(); + m_dispatcherSemaphore.acquire(); + wakeUp(); + } + + return QUnixEventDispatcherQPA::select(nfds, readfds, writefds, exceptfds, timeout); +} + +bool QAndroidEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + if (m_goingToStop.load()) { + return QUnixEventDispatcherQPA::processEvents(flags /*| QEventLoop::ExcludeUserInputEvents*/ + | QEventLoop::ExcludeSocketNotifiers + | QEventLoop::X11ExcludeTimers); + } else { + return QUnixEventDispatcherQPA::processEvents(flags); + } +} + + +QAndroidEventDispatcherStopper *QAndroidEventDispatcherStopper::instance() +{ + static QAndroidEventDispatcherStopper androidEventDispatcherStopper; + return &androidEventDispatcherStopper; +} + +void QAndroidEventDispatcherStopper::startAll() +{ + QMutexLocker lock(&m_mutex); + if (started) + return; + + started = true; + foreach (QAndroidEventDispatcher *d, m_dispatchers) + d->start(); +} + +void QAndroidEventDispatcherStopper::stopAll() +{ + QMutexLocker lock(&m_mutex); + if (!started) + return; + + started = false; + foreach (QAndroidEventDispatcher *d, m_dispatchers) + d->stop(); +} + +void QAndroidEventDispatcherStopper::addEventDispatcher(QAndroidEventDispatcher *dispatcher) +{ + QMutexLocker lock(&m_mutex); + m_dispatchers.push_back(dispatcher); +} + +void QAndroidEventDispatcherStopper::removeEventDispatcher(QAndroidEventDispatcher *dispatcher) +{ + QMutexLocker lock(&m_mutex); + m_dispatchers.erase(std::find(m_dispatchers.begin(), m_dispatchers.end(), dispatcher)); +} + +void QAndroidEventDispatcherStopper::goingToStop(bool stop) +{ + QMutexLocker lock(&m_mutex); + foreach (QAndroidEventDispatcher *d, m_dispatchers) + d->goingToStop(stop); +} diff --git a/src/plugins/platforms/android/qandroideventdispatcher.h b/src/plugins/platforms/android/qandroideventdispatcher.h new file mode 100644 index 0000000000..8d1bcf2122 --- /dev/null +++ b/src/plugins/platforms/android/qandroideventdispatcher.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2014 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDEVENTDISPATCHER_H +#define QANDROIDEVENTDISPATCHER_H + +#include +#include +#include + +class QAndroidEventDispatcher : public QUnixEventDispatcherQPA +{ + Q_OBJECT +public: + explicit QAndroidEventDispatcher(QObject *parent = 0); + ~QAndroidEventDispatcher(); + void start(); + void stop(); + + void goingToStop(bool stop); + +protected: + int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timespec *timeout); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + +private: + QAtomicInt m_stopRequest; + QAtomicInt m_goingToStop; + QSemaphore m_dispatcherSemaphore, m_stopperSemaphore; +}; + +class QAndroidEventDispatcherStopper +{ +public: + static QAndroidEventDispatcherStopper *instance(); + void startAll(); + void stopAll(); + void addEventDispatcher(QAndroidEventDispatcher *dispatcher); + void removeEventDispatcher(QAndroidEventDispatcher *dispatcher); + void goingToStop(bool stop); + +private: + QMutex m_mutex; + bool started = true; + QVector m_dispatchers; +}; + + +#endif // QANDROIDEVENTDISPATCHER_H diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index d6d7d3b173..829227f81c 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -47,15 +47,14 @@ #include #include -#include #include - #include #include #include #include "androidjnimain.h" #include "qabstracteventdispatcher.h" +#include "qandroideventdispatcher.h" #include "qandroidplatformbackingstore.h" #include "qandroidplatformaccessibility.h" #include "qandroidplatformclipboard.h" @@ -236,7 +235,7 @@ QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *wind QAbstractEventDispatcher *QAndroidPlatformIntegration::createEventDispatcher() const { - return createUnixEventDispatcher(); + return new QAndroidEventDispatcher; } QAndroidPlatformIntegration::~QAndroidPlatformIntegration() -- cgit v1.2.3