Работа с таймером в Sailfish OS на долгих интервалах времени

    Введение


    Довольно часто, при реализации какой-либо логики в приложении, возникает потребность в срабатывании некоторой функции через определённый промежуток времени. Наиболее очевидным примером такой потребности является приложение таймера. Например, 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.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 2

      0

      Сходу вопрос: на каком физическом телефоне гоняете Sailfish?

        0
        Jolla C и Inoi R7.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое