Документация
ОС Аврора 5.1.5

Клиентский API RuntimeManager

RuntimeManager — это компонент в программном обеспечении, который отвечает за управление временем жизни приложения и фоновых задач. Он содержит набор функций для управления жизненным циклом приложения, включая запуск и остановку выполнения программы.

Не рекомендуется использовать опцию экспорта интерфейсов D-Bus, поскольку она позволяет запускать приложение напрямую. Предлагаемая альтернатива: интенты.

Через RuntimeManager приложение имеет возможность создавать фоновые задачи, независимые от жизненного цикла приложения. Он также предоставляет механизмы взаимодействия приложений, например, интенты или обработка URI.

Также RuntimeManager обеспечивает возможность клиентам отслеживать жизненный цикл приложений и сервисов в среде выполнения.

D-Bus-интерфейс Intents предназначен для работы с интентами.

Среда приложения

RuntimeManager установит все переменные окружения, необходимые для правильного выполнения приложения, включая:

  • AURORA_APP_ID — содержит идентификатор (<org-name>.<app_name>) текущего приложения.
  • AURORA_TASK_ID — идентификатор фоновой задачи. Если один и тот же двоичный файл используется как для приложения, так и для его фоновых задач, эту переменную можно использовать, чтобы понять, в каком режиме выполняется процесс.
  • AURORA_TASK_FD — файловый дескриптор сокета-прослушивателя. Приложение или другие фоновые задачи могут подключаться к этому сокету для обмена данными.
  • AURORA_APP_INSTANCE_ID — содержит идентификатор приложения в сочетании с числовым идентификатором экземпляра (<org-name>.<app_name>.<instance_id>), где значение instance_id формируется как instance_<уникальное число>, для приложений с несколькими экземплярами; для приложений с одним экземпляром это будет то же самое, что и AURORA_APP_ID. Только в некоторых случаях может понадобиться передать AURORA_APP_INSTANCE_ID экземпляра приложения, которое вручную запросило задание.

ID приложения и ID экземпляра приложения

Для поддержки запуска нескольких экземпляров приложения RuntimeManager использует понятие "ID экземпляра приложения", который представляет собой ID приложения. ID приложения должен быть уникальным для всех экземпляров данного приложения. Например, быть дополненным суффиксом .instance_<число>.

Это необходимо, чтобы разрешить каждому из экземпляров регистрировать имя на шине D-Bus для получения сообщения от менеджера.

RuntimeDispatcher API

RuntimeDispatcher API основан на Qt API для вызова и регистрации интентов.

Фоновые задачи для приложений

Это фоновые процессы, которые необходимы приложениям для обеспечения их собственной функциональности. Они необходимы для следующих типов задач:

  • периодические задачи — запускаются RuntimeManager с интервалами, определёнными приложением;
  • запланированные задачи — запускаются RuntimeManager в определённое приложением время;
  • управляемая событиями задача — задача будет запущена RuntimeManager при выполнении определённых условий. Если условия больше не выполняются, задача может быть остановлена или заморожена;
  • рабочая задача — запускается по явному запросу приложения.

Во всех случаях они должны быть объявлены в desktop-файле приложения путём добавления раздела:

[X-Task <TASK_ID>]
Type=[periodic|worker|location|push|<more-domain-specific-events>]

# Строка MimeType позволяет дополнительно добавлять обработчики для доменов у приложений
# Например, данное поле позволяет добавить приложение как обработчик домена qr.nspk.ru
# Тогда при переходе в браузере на https://qr.nspk.ru пользователю будет предложено перейти в приложение, 
# которое зарегистрировало себя как обработчик данного домена (предполагается, что приложение,
# добавляя такой обработчик, действительно знает, как обрабатывать данный домен). 
# Данный механизм работает для всех доменов.
MimeType=x-url-handler/qr.nspk.ru;...

# В более общем случае это поле может быть использовано, когда приложение регистрирует себя обработчиком
# одного типа файлов, например image/jpeg.
# Cписок наиболее часто используемых Mime-типов можно найти здесь:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types.
MimeType=image/jpeg;...
 
# Строка Exec может быть опущена, и в этом случае будет запущен
# тот же исполняемый файл, что и приложение.
Exec=/usr/bin/runner
 
# Строка разрешений может быть опущена, и в этом случае задача будет иметь
# только базовый профиль задачи
Permissions=Internet;...
 
# Подробнее о разделе Conditions в разделе "Условия исполнения"
Conditions=idle

Периодические задачи

Эти задачи используются для выполнения некоторой (короткой) работы через определённые промежутки времени. В desktop-файле должны быть указаны максимальные временные рамки, в течение которых задача будет выполняться, но расписание также может быть скорректировано приложением или самой задачей при условии, что указанное расписание не должно предоставлять процессу более широкий график, чем тот, который указан в desktop-файле (это позволяет менеджеру пакетов проверить desktop-файл на предмет злоупотреблений и может быть использовано для того, чтобы дать пользователю представление о том, насколько ресурсоёмкой является задача).

Задача не должна делать никаких предположений о механизме, который будет использоваться для её запуска: это может быть то, что процесс прерывается и перезапускается при каждом "тике" расписания, или что один и тот же процесс замораживается/размораживается в соответствии с расписанием.

Можно указать условия, препятствующие запуску задачи, когда она не сможет выполнить свою работу.

Условия исполнения

Разработчики приложений могут добавить строку "Conditions" в свой файл desktop, чтобы указать, что задача должна выполняться только при определённых условиях.

Например:

# Двоеточие можно использовать для указания дополнительного условия, например  "internet:wifi"
Conditions=<condition1>;<condition2>;<condition3> 

Условие AND-ed (&&) используется для комбинирования двух или более условий и означает, что задача будет выполнена только при соблюдении всех условий.

Список поддерживаемых условий:

  • internet — выполняется при наличии подключения к Интернету. Доступны следующие дополнительные условия:

    • Wi-Fi — подключение осуществляется по сети Wi-Fi;
    • ethernet — подключение осуществляется через адаптер ethernet;
    • wifi-or-ethernet;
    • cellular — подключение осуществляется по сети сотовой связи;
  • idle — значение true, когда устройство находится в режиме ожидания (пользователь не использует его активно);

  • charging — значение true, когда устройство заряжается;

  • battery-not-low — батарея не разряжена.

Взаимодействие с приложениями

Схемы URI

Нет никаких ограничений на то, какие URI могут быть отправлены в уведомлении, при условии, что их размер не превышает 2048 байт.

С другой стороны, приложения, желающие обрабатывать уведомления, могут зарегистрироваться в качестве обработчиков только для следующих схем URI:

  • любой схемы, одобренной IANA, со статусом "постоянный" или "временный";
  • любая схема, начинающаяся с "aurora-<название_организации>.". Это позволяет приложениям определять свою собственную схему, гарантируя при этом отсутствие конфликтов с другими приложениями.

Доступные методы IPC

Приложение может взаимодействовать со своей фоновой задачей с помощью следующих методов IPC:

  • обычные файлы, поскольку приложение и задача совместно используют общий вид файловой системы;
  • сокеты с поддержкой файлов. Рекомендуется, чтобы сокет обслуживался фоновой задачей, поскольку приложение может быть закрыто/заморожено в любой момент;
  • разделяемая память (в POSIX, то есть shm_open() и friends), но только в том случае, если имя области SHM начинается с идентификатора приложения;
  • безымянные семафоры (sem_init());
  • именованные семафоры, если их название начинается с идентификатора приложения;
  • D-Bus: фоновые задачи могут вызывать методы в приложении (приложение регистрирует применяемые методы как имя службы). Важно, что вызов методов не приведёт к автоматическому запуску приложения, если оно не запущено.

Библиотека и API

Runtime manager предоставляет библиотеку, чтобы запускать интенты и фоновые задачи:

  • чтобы запускать фоновые задачи, используется класс Task:
    • для периодических задач есть класс Schedule, через который описывается расписание;
  • если разработчик хочет использовать один и тот же бинарный файл и для приложения, и для фоновой задачи, методы onApplicationStarted и onTaskStarted в классе RuntimeDispatcher помогают определить, если программа запускалась как приложение или как фоновая задача.

Управление фоновой задачей

Данный фрагмент кода иллюстрирует создание и управление задачей в фоновом режиме, а также отображение QML-интерфейса с помощью RuntimeDispatcher.

#include <RuntimeManager/RuntimeDispatcher>
#include <RuntimeManager/Task>

int main(int argc, char *argv[])
{
    QGuiApplication *app = Aurora::Application::application(argc, argv);

    app->setOrganizationName(QStringLiteral("ru.omp"));
    app->setApplicationName(QStringLiteral("RMWorkerTaskExample"));

    qDebug() << "Started with params" << app->arguments();

    RuntimeDispatcher *dispatcher = RuntimeDispatcher::instance();
    dispatcher->onTaskStarted("Writer", [app](const QString & /*taskID*/) {
        // We are the background task
        QString filePath = Aurora::Application::cacheDir().filePath(
            "worker.txt");
        QFile workerFile(filePath);
        if (!workerFile.open(QIODevice::WriteOnly | QIODevice::Append)) {
            qWarning() << "Could not open worker file";
            QCoreApplication::exit(EXIT_FAILURE);
            return;
        }
        workerFile.write("\n=====================================\n\n");
        for (int i = 0; i < 1000; i++) {
            QDateTime now = QDateTime::currentDateTime();
            workerFile.write(now.toString().toUtf8() + '\n');
            workerFile.flush();
            QThread::sleep(1);
        }
        QCoreApplication::exit(EXIT_SUCCESS);
    });
    dispatcher->onApplicationStarted([app]() {
        Task task("Writer");
        task.withArguments({"--worker"})
            .withStartDelay(10)
            .withMaximumRunningTime(45)
            .withPriority(10)
            .start();

        QString path = QLatin1String("MainPage.qml");

        QQuickView *view = Aurora::Application::createView();
        view->setSource(Aurora::Application::pathTo(path));
        view->show();
        view->setTitle("RMWorkerTaskExample");
    });

    return app->exec();
}

Вызов интента

В данном фрагменте кода происходит вызов и обработка интентов Start и OpenURI с использованием Invoker.

#include <RuntimeManager/IntentsInvoker>

namespace {
    const QString intentsHintPreferredHandler = QStringLiteral("preferredHandler");
    const QString intentsIntentNameStart = QStringLiteral("Start");
    const QString intentsIntentNameOpenURI = QStringLiteral("OpenURI");
}

namespace Intent {
    IntentDefault::IntentDefault(QObject* parent)
        : QObject(parent) 
    {
        
        connect(
            &IntentsInvoker::instance(), 
            &Invoker::IntentsInvoker::errorInfo,
            this, 
            &IntentDefault::errorInfo,
            Qt::QueuedConnection
        );
    }

    void IntentDefault::registerQmlType() 
    {
        qmlRegisterType<IntentDefault>("ru.auroraos.IntentDefault", 1, 0, "IntentDefault");
    }

    void IntentDefault::invokeStart(const QString& binName) 
    {
        QJsonObject hints = {
            {intentsHintPreferredHandler, binName},
        };
        QJsonObject params = {};
        IntentsInvoker::instance().invoke(intentsIntentNameStart, hints, params);
    }

    void IntentDefault::invokeOpenURI() 
    {
        QJsonObject hints = {};
        QJsonObject params = {
            {"uri", "tel:+79996660013"},
        };
        IntentsInvoker::instance().invoke(intentsIntentNameOpenURI, hints, params);
    }
}

Обработка интентов

Если приложение способно обрабатывать интенты, то это должно быть объявлено в desktop-файле:

Intents=<intent-name>[/<detail>];<intent-name>...
# Пример:
Intents=X-TextToSpeech;X-SpeechToText

Пример регистрации интента:

auto *dispatcher = RuntimeManager::RuntimeDispatcher::instance();
dispatcher->registerIntent(QStringLiteral("X-MyIntent"),
                           [&dispatcher](const QJsonObject &params,
                                        const RuntimeDispatcher::HandlerCb &reply) {
    // Сюда следует поместить код для обработки
    QJsonObject response = { ... };
    // Отправить данные ответа, используя обратный вызов, который получен в качестве параметра reply метода-обработчика.
    // Этот обратный вызов также может быть вызван асинхронно
    reply(response);
});

После того, как выполнены все важные действия по регистрации интентов, можно зарегистрировать службы D-Bus:

Error error = dispatcher->startService();
if (error)
    return EXIT_FAILURE;

Интент Start всегда регистрируется неявно, как и интент HandleURI, если приложение объявляет поддержку некоторых типов MIME.

Интент Start:

Явно зарегистрирован? Единичный экземпляр? Вызов интента Start
Нет Нет Порождение invoker
Нет Да Порождение invoker --single-instance
Да Нет Порождение invoker + Start в D-Bus
Да Да Порождение invoker --single-instance + Start в D-Bus

Интент OpenURI:

Явно зарегистрирован? Зарегистрирован типы MIME? Единичный экземпляр? Вызов интента OpenURI
Нет Нет Да или Нет Не вызывается
Нет Да Да или Нет Порождение invoker ... $URI
Нет Да Да Ошибка валидации
Да Нет Да или Нет Ошибка валидации
Да Да Да или Нет Порождение invoker [--single-instance] + HandleURI в D-Bus

Если интенты Start и OpenURI были явно регистрированы через API, тогда их вызов будет осуществляться через callback, указанный в registerIntent; если нет, URI, переданные во время вызова метода OpenURI данные, будут передаваться приложению как параметры запуска.

Мы используем cookies для персонализации сайта и его более удобного использования. Вы можете запретить cookies в настройках браузера.

Пожалуйста ознакомьтесь с политикой использования cookies.