Введение
Довольно часто, при реализации какой-либо логики в приложении, возникает потребность в срабатывании некоторой функции через определённый промежуток времени. Наиболее очевидным примером такой потребности является приложение таймера. Например, cooktimer или Saildoro.
Как было сказано в одной из предыдущих статей, для добавления таймера в приложение на Sailfish OS можно использовать стандартный элемент
Timer
или его C++ аналог — QTimer
. Однако, по умолчанию, работа этих таймеров приостанавливается на долгих промежутках времени из-за того, что устройство может уйти в «сон». Как раз с этой проблемой и столкнулись разработчики двух приложений, упомянутых выше.В данной статье представлен стандартный для системы, но к сожалению недокументированный способ обработки такого поведения Sailfish OS.
Начальная точка
В качестве отправной точки будет рассматриваться «абстрактное» приложение для Sailfish OS, в котором требуется срабатывание некоторого функционала через длительный промежуток времени. При этом работа таймера описывается не в коде QML, а в классе на C++:
Header
class TimerWrapper : public QObject
{
Q_OBJECT
public:
// Конструктор и деструктор
explicit TimerWrapper(QObject *parent = 0);
~TimerWrapper();
Q_INVOKABLE void start(int interval); // Метод для запуска таймера
Q_INVOKABLE void stop(); // Метод для остановки таймера
signals:
void pomodoroFinished(int start, int end); // Сигнал остановки таймера
void activeChanged(); // Сигнал смены состояния таймера
private:
QTimer *_timer; // Объект таймера
int _startTime; // Время запуска таймера
};
Source
#include "timerwrapper.h"
/**
* Конструктор таймера.
*/
TimerWrapper::TimerWrapper(QObject *parent) : QObject(parent) {
_timer = new QTimer(this); // Создание объекта таймера
_timer->setSingleShot(true); // Отключение автоматического возобновления таймера
// После остановки таймера посылаются сигналы смены состояния и завершения работы
connect(_timer, &QTimer::timeout, [=]() {
emit activeChanged();
eemit pomodoroFinished(_startTime, QDateTime::currentDateTime().toMSecsSinceEpoch());
});
}
/**
* Деструктор таймера.
*/
TimerWrapper::~TimerWrapper() {
delete _timer;
_timer = nullptr;
}
/**
* Метод для начала работы таймера.
* @:param: interval - длительность работы таймера в миллисекундах
*/
void TimerWrapper::start(int interval) {
_startTime = QDateTime::currentMSecsSinceEpoch(); // Сохранение времени начала
_timer->start(interval); // Запуск таймера
emit activeChanged(); // Сигнал о смене состояния таймера
}
/**
* Метод для остановки таймера.
*/
void TimerWrapper::stop() {
_timer->stop(); // Остановка таймера
emit activeChanged(); // Сигнал о смене состояния
}
Объект такого класса должен быть зарегистрирован в QML:
main.cpp
#ifdef QT_QML_DEBUG
#include <QtQuick>
#endif
#include <QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
#include <QScopedPointer>
#include <sailfishapp.h>
#include "timerwrapper.h"
int main(int argc, char *argv[]) {
// Создание объекта приложения
QScopedPointer<QGuiApplication> application(SailfishApp::application(argc, argv));
// Создание объекта для отображения интерфейса
QScopedPointer<QQuickView> view(SailfishApp::createView());
// Создание объекта таймера
QScopedPointer<TimerWrapper> timer(new TimerWrapper(view.data()));
// Регистрация объекта таймера
view->rootContext()->setContextProperty("timer", timer.data());
// Объявление пути к стартовому QML-файлу
view->setSource(SailfishApp::pathTo("qml/harbour-application.qml"));
// Отображение интерфейса
view->show();
// Запуск приложения
return application->exec();
}
При таком подходе, как упоминалось во введении, на длительных интервалах времени может наблюдаться приостановка работы таймера.
Решение
Первый вариант предотвращения засыпания таймера был предложен в почтовой рассылке разработчиков и прижился в приложении cooktimer. Здесь предлагается завести в явном виде дополнительный таймер, который раз в минуту вызывает D-Bus метод
req_display_cancel_blanking_pause
, чтобы предотвратить засыпание устройства. Очевидно, что такая реализация неоптимальна и громоздка. Во-первых, при использовании такого подхода быстрее разряжается батарея устройства. Во-вторых, в проекте появляется второстепенный код, которого можно избежать.А избежать использования второстепенного кода можно потому, что Sailfish OS уже предоставляет два возможных решения поставленной проблемы: элементы
ScreenBlank
и KeepAlive
.Использование первого подхода подразумевает постоянно активный экран. Это рабочий, но прямолинейный подход, активно потребляющий заряд аккумулятора устройства. Таким образом, использовать его можно, но в ограниченном круге ситуаций.
import QtQuick 2.0 // Подключение модуля для поддержки стандартных элементов QML
import Sailfish.Silica 1.0 // Подключение модуля для поддержки Sailfish OS UI
import Sailfish.Media 1.0 // Подключение модуля для поддержки элемента ScreenBlank
ApplicationWindow // Объявление главного окна приложения
{
initialPage: Component { FirstPage { } } // Объявление главной страницы приложения
cover: Qt.resolvedUrl("cover/CoverPage.qml") // Объявление обложки приложения
ScreenBlank { // Объявление элемента для предотвращения засыпания устройства
id: screenBlank // Идентификатор для обращения
suspend: true // Экран постоянно активирован
}
}
В свою очередь использование элемента
KeepAlive
является более демократичным подходом. Он в меньшей степени потребляет заряд батареи, так как не держит экран устройства постоянно включенным, и в то же время, либо не даёт уйти устройству в глубокий сон, либо «будит» его в определённый момент времени, благодаря чему таймер будет продолжать работу и на долгих промежутках времени.import QtQuick 2.0 // Подключение модуля для поддержки стандартных элементов QML
import Sailfish.Silica 1.0 // Подключение модуля для поддержки Sailfish OS UI
import org.nemomobile.keepalive 1.1 // Подключение модуля для поддержки элемента KeepAlive
ApplicationWindow // Объявление главного окна приложения
{
initialPage: Component { FirstPage { } } // Объявление главной страницы приложения
cover: Qt.resolvedUrl("cover/CoverPage.qml") // Объявление обложки приложения
KeepAlive { // Объявление элемента для предотвращения засыпания устройства
id: keepAlive // Идентификатор для обращения
enabled: true // Устройство не уходит в глубокий сон
}
}
Стоит заметить, что в принципе работы всех трёх упомянутых способах лежит регулярное обращение к системным методам D-Bus, что обсуждалось в одной из предыдущих статей.
Заключение
В рамках данной небольшой заметки описаны три возможных способа предотвращения глубокого засыпания устройства. Можно сделать вывод, что для фоновых задач (например, таймер) оптимально использовать элемент
KeepAlive
, а если имеется необходимость постоянного отображения информации пользователю, то ScreenBlank
.
monah_tuk
Сходу вопрос: на каком физическом телефоне гоняете Sailfish?
osanwe Автор
Jolla C и Inoi R7.