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>
This commit is contained in:
parent
7f9398fd4d
commit
734c126826
@ -36,6 +36,14 @@
|
||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
|
||||
-->
|
||||
<!-- Splash screen -->
|
||||
|
||||
<!-- Background running -->
|
||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||
application still try to draw after
|
||||
"applicationStateChanged(Qt::ApplicationSuspended)"
|
||||
signal is sent! -->
|
||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||
<!-- Background running -->
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="14"/>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
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)
|
||||
|
@ -120,6 +120,7 @@ namespace QtAndroid
|
||||
const char *qtTagText();
|
||||
|
||||
QString deviceName();
|
||||
bool blockEventLoopsWhenSuspended();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
149
src/plugins/platforms/android/qandroideventdispatcher.cpp
Normal file
149
src/plugins/platforms/android/qandroideventdispatcher.cpp
Normal file
@ -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);
|
||||
}
|
89
src/plugins/platforms/android/qandroideventdispatcher.h
Normal file
89
src/plugins/platforms/android/qandroideventdispatcher.h
Normal file
@ -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
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user