Qt 5.7 android services
-
Hi,
For the last week or so, I've tried to get my head around how to use the Android services introduced in Qt 5.7. What I would like to do is to have some code run in the background, even if the app is closed.
I'm afraid I can't figure out how to use this feature based on the documentation, and as it's quite new I can't find any good examples on the web either. I did find this github project, and have been using it as a template to get my head around services.
I tried to create a single program consisting of a QObject containing an integer which is displayed on the screen, and then having the background service increment the value of this integer every second.
My code below:
// main.cpp #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QDebug> #include <QtQml/QQmlContext> #include "model.h" #include "myservice.h" #ifdef Q_OS_ANDROID #include <QAndroidJniObject> #include <QtAndroid> #endif int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; qmlRegisterUncreatableType<Model>("Services", 1,0, "Model", "Stuff"); Model* model = Model::getInstace(); if (QCoreApplication::arguments().count() > 1){ //qDebug() << "I am the service"; MyService* service = new MyService(model); } else{ qDebug() << "I am the application"; engine.rootContext()->setContextProperty("model", QVariant::fromValue(model)); engine.addImportPath(QStringLiteral("qrc:/")); engine.load(QUrl(QStringLiteral("qrc:/source/qml/main.qml"))); } return app.exec(); }
// model.h #ifndef MODEL_H #define MODEL_H #include <QObject> class Model : public QObject { Q_OBJECT Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged) public: static Model* getInstace(); Model(QObject *parent = nullptr); int x() const { return m_x; } public slots: void setX(int x) { if (m_x == x) return; m_x = x; emit xChanged(x); } signals: void xChanged(int x); private: static Model* s_model; int m_x; }; #endif // MODEL_H
// model.cpp #include "model.h" Model *Model::getInstace() { if(s_model == nullptr) { s_model = new Model(); } return s_model; } Model::Model(QObject *parent) : QObject(parent) { setX(1); } Model* Model::s_model = nullptr;
// myservice.h #ifndef MYSERVICE_H #define MYSERVICE_H #include <QObject> #include "model.h" class MyService : public QObject { Q_OBJECT public: explicit MyService(Model* m, QObject *parent = 0); Model* getModel() { return m_; } signals: public slots: private: Model* m_; void updateModel(); }; #endif // MYSERVICE_H
// myservice.cpp #include "myservice.h" #include <QTimer> MyService::MyService(Model *m, QObject *parent) : QObject(parent) { m_ = m; QTimer* timer = new QTimer(parent); connect(timer, &QTimer::timeout, this, &MyService::updateModel); timer->start(1000); } void MyService::updateModel() { Model* m = getModel(); m->setX(m->x() + 1); }
// AndroidManifest.xml <?xml version='1.0' encoding='utf-8'?> <manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto"> <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --"> <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.example.MyCustomAppActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <!-- Application arguments --> <!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ --> <!-- Application arguments --> <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> <meta-data android:name="android.app.repository" android:value="default"/> <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/> <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/> <!-- Deploy Qt libs as part of package --> <meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/> <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/> <!-- Run with local libs --> <meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/> <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/> <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/> <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/> <!-- Messages maps --> <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/> <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/> <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/> <!-- Messages maps --> <!-- Splash screen --> <!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ --> <!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ --> <!-- 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 --> <!-- auto screen scale factor --> <meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/> <!-- auto screen scale factor --> <!-- extract android style --> <!-- available android:values : * full - useful QWidget & Quick Controls 1 apps * minimal - useful for Quick Controls 2 apps, it is much faster than "full" * none - useful for apps that don't use any of the above Qt modules --> <meta-data android:name="android.app.extract_android_style" android:value="full"/> <!-- extract android style --> </activity> <!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices --> <service android:process=":qt" android:enabled="true" android:name="org.qtproject.example.MyCustomAppService"> <!--<service android:process=":qt" android:enabled="true" android:name="org.qtproject.qt5.android.bindings.QtService">--> <!-- android:process=":qt" is needed to force the service to run on a separate process than the Activity --> <!-- Application arguments --> <meta-data android:name="android.app.arguments" android:value="-service"/> <!-- Application arguments --> <!-- If you are using the same application (.so file) for activity and also for service, then you need to use *android.app.arguments* to pass some arguments to your service in order to know which one is which --> <!-- Ministro --> <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> <meta-data android:name="android.app.repository" android:value="default"/> <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/> <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/> <!-- Ministro --> <!-- Deploy Qt libs as part of package --> <meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/> <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/> <!-- Deploy Qt libs as part of package --> <!-- Run with local libs --> <meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/> <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/> <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/> <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/> <!-- Run with local libs --> <!-- Messages maps --> <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/> <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/> <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/> <!-- Messages maps --> <!-- Background running --> <meta-data android:name="android.app.background_running" android:value="true"/> <!-- Background running --> </service> </application> <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/> <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application. Remove the comment if you do not require these default permissions. --> <!-- %%INSERT_PERMISSIONS --> <!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application. Remove the comment if you do not require these default features. --> <!-- %%INSERT_FEATURES --> </manifest>
Thank you
-
i have the same problem.
i cannot search a good example about qt android service stuff. -
@Audun
and your problem now is?? What isn't working?
At least a description would be useful to provide help. -
Since the original poster didn't answer anymore, I am taking this over. I have spent a week now, to get a service running on Android. Basically, I want to make the Google Firebase notification samples work:
• Firebase setup
• Messaging sample app
• Service java sample codeI have made some changes to the AndroidManifest.xml to register 2 services:
<service android:name=".MyFirebaseMessagingService"> <!-- Maybe prefix with <path.to.project>? --> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <service android:name=".MyFirebaseInstanceIDService"> <!-- Maybe prefix with <path.to.project>? --> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT" /> </intent-filter> </service>
I think I have managed to add 2 Firebase Services by adding some magical lines to a build.gradle file (will they be permanent?):
apply plugin: 'com.android.application' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.firebase:firebase-core:9.4.0' /* THIS ONE */ compile 'com.google.firebase:firebase-messaging:9.4.0' /* AND THIS ONE */ }
And, at the end of the gradle file:
apply plugin: 'com.google.gms.google-services'
but I can*t figure out what to do with the java files. Just adding them to the SOURCES variable in the .pro file, like I did for iOS, leads to an error message "There is no Java compiler", which is of course very wrong. Adding them to DISTFILES or OTHER_FILES will have them copied to the build folder, but there they will be ignored. They are missing from the resulting classes.dex as stated by the following runtime error upon startup:
E/AndroidRuntime( 7726): java.lang.RuntimeException: Unable to instantiate service <path.to.project>.MyFirebaseInstanceIDService: java.lang.ClassNotFoundException: Didn't find class "<path.to.project>.MyFirebaseInstanceIDService" on path: DexPathList[[zip file "/data/app/<path.to.project>-24.apk"],nativeLibraryDirectories=[/data/app-lib/<path.to.project>-24, /vendor/lib, /system/lib]]
I am running out of ideas, the topic of adding java code to implement Android services is not covered in the Qt documentation. The sample on http://doc.qt.io/qt-5/android3rdpartylibs.html does not work anymore, because of massive changes in the current Android SDK.
-
@Orkus said in Qt 5.7 android services:
but I can*t figure out what to do with the java files.
put them into the
src
folder of your path specified in theANDROID_PACKAGE_SOURCE_DIR
variable in your pro file. the Qt deploy tool will then copy them into your app. -
There was no "src" directory at that location. So I created one and moved the java files there. I did not mention them in the .pro file. The files have not shown up in the project view within QtCreator. I executed qmake, rebuilt and ran the app. Same result (RuntimeException). Can I trace or debug somehow, what's going on?
After adding them to the DISTFILES variable, they got copied to the build directory and ignored afterwards. The Qt deploy tool obviously does not do anything with them. Shouldn't they be compiled into the dalvik executable?
OK, I found them as ".class" files in <build-folder>/android-build/build/intermediates/classes/debug/... among other java files like "BuildConfig.class". So maybe, some identifier is wrong or something.