Android Services▲
Starting with Qt 5.7, you can create Android services using Qt. A service is a component that runs in background, so, it has no user interface. It is useful to perform long-term operations such as logging GPS, waiting for social media notifications, and so on. A service will continue to run even if the application that started it exits.
Assemble the Service▲
To get started, create an Android package directory as instructed in Qt Creator: Deploying Applications to Android Devices. This directory contains the AndroidManifest.xml file. Inside the package directory, create a src directory, where all your Java packages and classes will be created.
Create the Service Class▲
You can create a service by extending the class QtService or Android: Service to your Java class. Depending on whether you want to use Qt features in your service or call native C++ functions from Java, you need to extend either QtService or Service. Let's start with a simple service, as follows:
import
android.content.Context;
import
android.content.Intent;
import
android.util.Log;
import
org.qtproject.qt.android.bindings.QtService;
public
class
QtAndroidService extends QtService
{
private
static
final
String TAG =
"QtAndroidService"
;
@Override
public
void
onCreate() {
super.onCreate();
Log.i(TAG, "Creating Service"
);
}
@Override
public
void
onDestroy() {
super.onDestroy();
Log.i(TAG, "Destroying Service"
);
}
@Override
public
int
onStartCommand(Intent intent, int
flags, int
startId) {
int
ret =
super.onStartCommand(intent, flags, startId);
// Do some work
return
ret;
}
}
Start the Service▲
Android allows starting services on demand or at boot time. You can do both using Qt as well.
Start a Service At Boot Time▲
To run a service at boot time, you need a BroadcastReceiver.
Create a custom Java class:
public
class
QtBootServiceBroadcastReceiver extends BroadcastReceiver {
@Override
public
void
onReceive(Context context, Intent intent) {
Intent startServiceIntent =
new
Intent(context, QtAndroidService.class
);
context.startService(startServiceIntent);
}
}
Add the following uses-permission in the body of the <manifest> section in the AndroidManifest.xml file:
&
lt;uses-
permission android:name=
"android.permission.RECEIVE_BOOT_COMPLETED"
/&
gt;
Also, add the receiver definition in the body of the <application> section:
&
lt;receiver android:name=
".QtBootServiceBroadcastReceiver"
&
gt;
&
lt;intent-
filter&
gt;
&
lt;action android:name=
"android.intent.action.BOOT_COMPLETED"
/&
gt;
&
lt;/
intent-
filter&
gt;
&
lt;/
receiver&
gt;
Android 8.0 introduced some limitations on running background services, which means using a nomal Service class might not work. For more information, see Android's recommendation to use either Android: Foreground services{Foreground services} or JobIntentService.
Manage the Service in AndroidMnifest.xml▲
For the service to be usable in an Android app, you must declare it in the AndroidManifest.xml file. Let's start with adding the service section:
-
When extending Service, just declare the service section as a normal Android service. Add the following inside the <application> section:
Sélectionnez&
lt;service android:name=
".QtAndroidService"
&
gt;&
lt;!--
Background running--&
gt;&
lt;meta-
data android:name=
"android.app.background_running"
android:value=
"true"
/&
gt;&
lt;!--
Background running--&
gt;&
lt;/
service&
gt;This way the service will start in the same process as QtActivity, which allows you to use native C++ calls from Java code. You can run it in a separate process but that way you cannot use any native calls for communication because the Qt libraries are not loaded for that process. To run on separate process, add this to the service tag:
Sélectionnezandroid
:
process=
":qt_service"
-
When extending QtService, you need to declare other items for loading all the necessary libs required for Qt, mainly the same items as in <activity> section for QtActivity. Add the following:
Sélectionnez&
lt;service android:process=
":qt_service"
android:name=
".QtAndroidService"
&
gt;&
lt;meta-
data android:name=
"android.app.lib_name"
android:value=
"service"
/&
gt;&
lt;meta-
data android:name=
"android.app.qt_sources_resource_id"
android:resource=
"@array/qt_sources"
/&
gt;&
lt;meta-
data android:name=
"android.app.repository"
android:value=
"default"
/&
gt;&
lt;meta-
data android:name=
"android.app.qt_libs_resource_id"
android:resource=
"@array/qt_libs"
/&
gt;&
lt;meta-
data android:name=
"android.app.bundled_libs_resource_id"
android:resource=
"@array/bundled_libs"
/&
gt;&
lt;!--
Deploy Qt libs as part of package--&
gt;&
lt;meta-
data android:name=
"android.app.bundle_local_qt_libs"
android:value=
"-- %%BUNDLE_LOCAL_QT_LIBS%% --"
/&
gt;&
lt;!--
Run with local libs--&
gt;&
lt;meta-
data android:name=
"android.app.use_local_qt_libs"
android:value=
"-- %%USE_LOCAL_QT_LIBS%% --"
/&
gt;&
lt;meta-
data android:name=
"android.app.libs_prefix"
android:value=
"/data/local/tmp/qt/"
/&
gt;&
lt;meta-
data android:name=
"android.app.load_local_libs_resource_id"
android:resource=
"@array/load_local_libs"
/&
gt;&
lt;meta-
data android:name=
"android.app.load_local_jars"
android:value=
"-- %%INSERT_LOCAL_JARS%% --"
/&
gt;&
lt;meta-
data android:name=
"android.app.static_init_classes"
android:value=
"-- %%INSERT_INIT_CLASSES%% --"
/&
gt;&
lt;!--
Run with local libs--&
gt;&
lt;!--
Background running--&
gt;&
lt;meta-
data android:name=
"android.app.background_running"
android:value=
"true"
/&
gt;&
lt;!--
Background running--&
gt;&
lt;/
service&
gt;
Make sure to define the following to run the service in the background:
&
lt;meta-
data android:name=
"android.app.background_running"
android:value=
"true"
/&
gt;
There are a few variations on how to declare services. Some of them are already used in the previous manifest snippet. Depending on your use case, run the service either in the same process as QtActivity or in a separate process.
Service in the Same Process as QtActivity▲
To run a service in the same process as QtActivity, declare the service header as follows:
&
lt;service android:name=
".QtAndroidService"
&
gt;
Service in Separate Process▲
To run a service in a dedicated process, declare the service header as follows:
&
lt;service android:process=
":qt_service"
android:name=
".QtAndroidService"
&
gt;
Qt loads the .so file defined in android.app.lib_name meta-data, and calls the main() function with all the arguments set in android.app.arguments meta-data. When running in a separate process, you can start the service using either the same lib file as the main activity or a separate lib file.
Use the Same .so Lib File▲
Using the same .so lib file as the main activity means the service will use the same entry point with an extra argument to distinguish it from the main activity. You can handle your application's execution in the main() function according the arguments provided. Add the following argument declaration to your service body:
&
lt;!--
Application arguments --&
gt;
&
lt;meta-
data android:name=
"android.app.arguments"
android:value=
"-service"
/&
gt;
&
lt;!--
Application arguments --&
gt;
Then make sure the service android.app.lib_name is the same as the main activity, add the following:
&
lt;meta-
data android:name=
"android.app.lib_name"
android:value=
"-- %%INSERT_APP_LIB_NAME%% --"
/&
gt;
When using the same .so lib file, your application's main() function is executed two times, one to start the main activity and the second time to start the service. Thus, you have to handle each execution according to the provided argument. One way to acheive that is as follows:
if
(argc &
lt;=
1
) {
// code to handle main activity execution
}
else
if
(argc &
gt; 1
&
amp;&
amp; strcmp(argv[1
], "-service"
) ==
0
) {
qDebug() &
lt;&
lt; "Service starting with from the same .so file"
;
QAndroidService app(argc, argv);
return
app.exec();
}
else
{
qWarning() &
lt;&
lt; "Unrecognized command line argument"
;
return
-
1
;
}
Use a Separate .so Lib File▲
In this case, you need to have a sub-project with a lib template that provides a different executable for the service. A sample project .pro is:
TEMPLATE =
lib
TARGET =
service
CONFIG +=
dll
QT +=
core androidextras
SOURCES +=
\
service_main.cpp
HEADERS +=
servicemessenger.h
In the service_main.cpp you could have the following:
#include <QDebug>
#include <QAndroidService>
int
main(int
argc, char
*
argv[])
{
qWarning() &
lt;&
lt; "Service starting from a separate .so file"
;
QAndroidService app(argc, argv);
return
app.exec();
}
Define the android.app.lib_name for the service in the AndroidManifest.xml:
&
lt;meta-
data android:name=
"android.app.lib_name"
android:value=
"service"
/&
gt;
Communication with the Service▲
Qt for Android offers a variety of inter-process communication (IPC) methods to communicate with Android Services. Depending on the structure of your project, you can use either native C++ calls from Java Service or Android BroadcastReceiver.
Native C++ Calls from Java Service▲
This can work with services running in the same process as QtActivity and even if Service is extended.
Using QAndroidBinder▲
QAndroidBinder is a convenience class that enables inter-process communication by implementing the most important methods in Android: Binder. It allows sending QByteArray or QVariant objects between processes.
Qt for Android has a limitation forcing the execution of only one service at a time when running multiple services in one process. Thus, it is recommended to run each service in its own process. For more information, see QTBUG-78009.