diff options
author | BogDan Vatra <bogdan@kde.org> | 2014-07-28 10:41:44 +0300 |
---|---|---|
committer | BogDan Vatra <bogdan@kde.org> | 2014-07-31 19:20:28 +0200 |
commit | 734c1268269a71b16829d36179e4a090f76c785e (patch) | |
tree | 619f318c2c2479845ca9c4a2cedce4ddd6711b8e /src/plugins | |
parent | 7f9398fd4d23469c77c6a53eac0819f92dfc3928 (diff) |
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 <thiago.macieira@intel.com>
Reviewed-by: Paul Olav Tvete <paul.tvete@digia.com>
Diffstat (limited to 'src/plugins')
6 files changed, 266 insertions, 7 deletions
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 <android/bitmap.h> #include <android/asset_manager_jni.h> #include "qandroidassetsfileenginehandler.h" +#include "qandroideventdispatcher.h" #include <android/api-level.h> #include <QtCore/private/qjnihelpers_p.h> @@ -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 <bogdan@kde.org> +** 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 <bogdan@kde.org> +** 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 <QtCore/QMutex> +#include <QtCore/QSemaphore> +#include <QtPlatformSupport/private/qunixeventdispatcher_qpa_p.h> + +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<QAndroidEventDispatcher *> 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 <QThread> #include <QOffscreenSurface> -#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> #include <QtPlatformSupport/private/qeglpbuffer_p.h> - #include <qpa/qwindowsysteminterface.h> #include <qpa/qplatformwindow.h> #include <qpa/qplatformoffscreensurface.h> #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() |