|
|||||||
Как создать Android-сервис с использованием Qt
Время создания: 15.01.2019 19:01
Автор: Xintrea
Текстовые метки: c++, java, qt, Android, service, андроид, сервис
Раздел: Компьютер - Программирование - Java - Java в Android
Запись: xintrea/mytetra_syncro/master/base/1547568115kz829ftfkd/text.html на raw.github.com
|
|||||||
|
|||||||
Оригинальное название статьи: Qt on Android: How to create an Android service using Qt Начиная с Qt 5.7, в данный фреймверк была добавлена возможность создавать Android-сервисы. В этой статье мы рассмотрим, как запускать сервисы и как организовать взаимодействие между двумя сервисами. Перед тем как мы начнем, следует сделать очень важное предупреждение об ресурсах (CPU, память и т.д.) используемых сервисом. Так как сервис запускается в фоновом режиме, и обычно работает очень длительное время, то при разработке следует уделить особое внимание качеству кода, чтобы сервис не растрачивал ресурсы системы и не тратил попусту заряд батареи мобильного устройства. Важное замечание для Qt 5.10 и выше Начиная с релиза Qt 5.10, необходимо использовать QAndroidService вместо QCoreApplication в реализации серверной части сервиса. Начало Шаг 1. Расширение QtService Каждый отдельный сервис, создаваемый в Android через Qt, должен иметь свой Java-класс, расширенный от специального Java-класса QtService. Поэтому первым шагом является создание такого сервиса: // java file goes in android/src/com/kdab/training/MyService.java package com.kdab.training; import org.qtproject.qt5.android.bindings.QtService;
public class MyService extends QtService { } Шаг 2. Добавление секции service в файл AndroidManifest.xml Следующим шагом является добавление серции service в файл AndroidManifest.xml. Для этого сначала нужно скопировать и вставить шаблон из https://wiki.qt.io/AndroidServices в файл AndroidManifest.xml, а затем прописать в атрибут android: name строку с именем вашего класса. Как это выглядит, показано в следующем фрагменте: <application ... > <!-- .... --> <service android:process=":qt" android:name=".MyService"> <!-- android:process=":qt" is needed to force the service to run on a separate process than the Activity -->
<!-- .... -->
<!-- Background running --> <meta-data android:name="android.app.background_running" android:value="true"/> <!-- Background running --> </service> <!-- .... --> </application> ВНИМАНИЕ: каждый отдельный Сервис или Активность Qt ДОЛЖНЫ выполняться в своем собственном процессе! Поэтому для каждого сервиса необходимо устанавливать разные значения атрибута android: process. Прим. переводчика: в каждом приложении может быть создано несколько сервисов, соответственно, в пределах тега <application ...> может находиться несолько тегов <service ...>. Шаг 3. Как запустить сервис? Теперь нужно решить, как запустить сервис. Есть два способа сделать это:
Ниже описано как сделать оба варианта. Запуск сервиса по требованию Это наиболее распространенный способ запуска сервиса. Чтобы запустить службу, вам просто нужно вызвать метод Context.startService(Intent intent) . Самый простой способ сделать это - добавить статический метод в MyService: // java file goes in android/src/com/kdab/training/MyService.java package com.kdab.training;
import android.content.Context; import android.content.Intent; import org.qtproject.qt5.android.bindings.QtService;
public class MyService extends QtService { public static void startMyService(Context ctx) { ctx.startService(new Intent(ctx, MyService.class)); } } Затем просто вызовите его из Qt, чтобы запустить его: QAndroidJniObject::callStaticMethod<void>("com/kdab/training/MyService", "startMyService", "(Landroid/content/Context;)V", QtAndroid::androidActivity().object()); Запуск сервиса во время загрузки устройства Этот метод используется довольно редко и полезен ТОЛЬКО в тех случаях, когда вам действительно нужно запустить службу во время загрузки устройства, в противном случае рекомендуется запускать ее по требованию. Сначала вам нужно добавить разрешение android.permission.RECEIVE_BOOT_COMPLETED в ваш файл AndroidManifest.xml: <application ... >
<!-- .... --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> </application> Затем в файл AndroidManifest.xml нужно добавить элемент получателя сигнала от системы о том, что загрузка устройства завершена (и можно запускать сервис): <application ... > <!-- .... --> <receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <!-- .... --> </application> И, наконец, нужно реализовать класс MyBroadcastReceiver, как показано в следующем фрагменте: public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent startServiceIntent = new Intent(context, MyService.class); context.startService(startServiceIntent); } } При создании данного получателя, он будет запускать сервис, реализация которого находится в классе MyService. Запуск будет происходить при приходе сообщения от системы android.intent.action.BOOT_COMPLETED, так как этот фильтр был прописан в файле манифеста. Шаг 4. Где разместить код QtService? Далее нужно решить, куда вы собираетесь поместить свой сервисный код. Фреймверк Qt и система сборки qmake поддерживает два варианта:
Ниже описаны оба варианта. Один и тот же файл *.so для кода приложения и для кода сервиса Поскольку будет один большой .so-файл, необходим способ узнать, как он будет работать: как Активность (то есть, как приложение пользователя) или как Сервис. Для этого вам просто нужно передать некоторые аргументы в вашу функцию main(). Файл AndroidManifest.xml позволяет легко сделать это: <service ... > <!-- ... --> <!-- Application arguments --> <meta-data android:name="android.app.arguments" android:value="-service"/> <!-- Application arguments --> <!-- ... --> </service> Кроме того, надо убедиться, что установлены одинаковые значения метаданных android.app.lib_name для сервиса (сервисов) и для Активности самого приложения: <service ... > <!-- ... --> <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> <!-- ... --> </service> Рекомендуется использовать этот метод компоновки сприложения в случае, если и само приложение, и сервис используют один и тот же большой кусок кода. Отдельные .so файлы для приложения и сервисов Второй вариант - создать отдельные файлы *.so для вашего приложения и для сервиса. Сначала вам нужно создать отдельный файл .pro для сервера: TEMPLATE = lib TARGET = server CONFIG += dll QT += core SOURCES += \ server.cpp Таким образом будет создаваться so-файл с точкой входа в виде функции main(): #include <QDebug>
int main(int argc, char *argv[]) { qDebug() << "Hello from service"; return 0 } И последнее, что нужно сделать, это разрешить загрузку .so-файла в виде сервиса: <service ... > <!-- ... --> <meta-data android:name="android.app.lib_name" android:value="server"/> <!-- ... --> </service> Внимание! Использование дальнейших рекомендаций под большим вопросом. Использование их приведет к привязке проекта к сырым технологиям, поддержка которых не гарантируется команией Qt. Использование QtRemoteObject для коммуникации между процессами Мы видели, как создавать и запускать сервисы Qt на Android, теперь давайте посмотрим, как установить связь между ними. Существует множество решений, но для любого проекта Qt я рекомендую вам использовать QtRemoteObject. Использование этого класса в Qt очень облегчает жизнь программиста! QtRemoteObjects - это пробный модуль Qt, разрабатываемый при поддержке компании Ford, используемый для удаленного взаимодействия объектов между процессами/устройствами. Данный модуль:
Как вы можете видеть, эта технология очень специфична для Qt. Давайте посмотрим, как добавить её в свои проекты и как использовать её. Получение QtRemoteObjects Проект QtRemoteObjects находится здесь: http://code.qt.io/cgit/qt/qtremoteobjects.git/ . Для его получания можно воспользоваться командами: $ git clone git://code.qt.io/qt/qtremoteobjects.git $ cd qtremoteobjects $ ~/Qt/5.10.1/android_armv7/bin/qmake -r && make && make install Если необходимо, нужно заменить путь ~/Qt/5.10.1/android_armv7 на вашу версию Qt и ваш Android ABI. Использование QtRemoteObjects Использовать QtRemoteObjects довольно просто, нужно сделать несколько несложных шагов: – Добавить QtRemoteObjects в ваш *.pro файл # ... QT += androidextras QT += remoteobjects # ... – Создать .rep файл(ы) class PingPong { SLOT(void ping(const QString &msg)); SIGNAL(pong(const QString &msg)); } – Добавить .rep файл(ы) в .pro-файл сервиса # ... REPC_SOURCE += pingpong.rep # ... – Добавить .rep файл(ы) в .pro файл клиента # ... REPC_REPLICA += pingpong.rep # ... – Сделать реализацию QtRemoteObjects на стороне сервера #include <QAndroidService> #include "rep_pingpong_source.h"
class PingPong : public PingPongSource { public slots: // PingPongSource interface void ping(const QString &msg) override { emit pong(msg + " from server"); } };
int main(int argc, char *argv[]) { QAndroidService app(argc, argv);
QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica"))); PingPong pingPongServer; srcNode.enableRemoting(&pingPongServer);
return app.exec(); } Теперь можно немного проверить код. Сначала необходимо реализовать все интерфейсы .rep ( PingPongSource ), затем экспортировать объект PingPong с помощью enableRemoting. – Релизация реплики QtRemoteObjects на стороне клиента #include "rep_pingpong_replica.h"
// .... QRemoteObjectNode repNode; repNode.connectToNode(QUrl(QStringLiteral("local:replica"))); QSharedPointer<PingPongReplica> rep(repNode.acquire<PingPongReplica>()); bool res = rep->waitForSource(); Q_ASSERT(res); QObject::connect(rep.data(), &PingPongReplica::pong, [](const QString &msg){ qDebug() << msg; }); rep->ping("Hello"); // .... Давайте проверим код. Он должен:
Как видите, использовать Qt + QtRemoteObject (намного???) проще, чем Java-сервисы Android + AIDL. Ограничения
Пожалуйста, используйте приведенные выше идентификаторы багов для сообщений об ошибках в багтрекере Qt, чтобы проголосовать за доработку необходимых вам задач. Те, у которых больше голосов (обычно), выполняются первыми! |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|