Skip to content

Qt on Android: How to create an Android service using Qt

Starting with Qt 5.7, we added the ability to create Android services using Qt. In this article we’re going to see how to get started and also how to communicate between the two.

Before we get started I want to add a big bold WARNING about the performance! Because the services are run in the background for a very long time, make sure your service doesn’t drain the device battery!

Getting started

Step I: Extend QtService

Every single Qt Android Service must have its own Service java class which extends QtService, so the first step is to create such a service:

// 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
{
}

Step II: Add the service section(s) to your AndroidManifest.xml file

The next step is to add the service section(s) to your AndroidManifest.xml file. To do that you first need to copy & paste the template from https://wiki.qt.io/AndroidServices to your AndroidManifest.xml file, then set android:name attribute with your service class name, as shown in the following snippet:

<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>

BE AWARE: Every single Qt service/activity MUST run in it’s own process! Therefore for each service you must set a different android:process attribute value.

Step III: How to start the service ?

Now you need to decide how to start the service. There are two ways to do it:

  • on demand
  • at boot time

We’re going to check them both:

Start the service on demand

This is the most common way to start your service(s). To start the service you just need to call Context.startService(Intent intent) method. The easiest way is to add a static method to your 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));
    }
}

Then simply call it from Qt to start it:

QAndroidJniObject::callStaticMethod<void>("com/kdab/training/MyService",
                                              "startMyService",
                                              "(Landroid/content/Context;)V",
                                              QtAndroid::androidActivity().object());

Start the service at boot time

This method is used quite seldom and is useful ONLY when you really need to run the service at boot time, otherwise I do recommend you to start it on demand.

First you need to add android.permission.RECEIVE_BOOT_COMPLETED permission to your AndroidManifest.xml file:

<application ... >

  <!-- .... -->
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
</application>

Then you need to add a receiver element to your AndroidManifest.xml file:

<application ... >
    <!-- .... -->
    <receiver android:name=".MyBroadcastReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    <!-- .... -->
</application>

And finally, you need to implement MyBroadcastReceiver class, as shown in the following snippet:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent startServiceIntent = new Intent(context, MyService.class);
        context.startService(startServiceIntent);
    }
}

Step IV: Where to put your Qt Service code?

Next you need to decide where you’re going to put your service code. Qt (and qmake) has two options for you:

  • in the same .so file with the application
  • in a separate .so file

We’re going to check them both:

Same .so for app & service(s)

Because you’ll have one big .so file, you need a way to know when it will run as an activity or as a service. To do that you just need pass some arguments to your main function. AndroidManifest.xml allows you to easily do that:

<service ... >
    <!-- ... -->
    <!-- Application arguments -->
    <meta-data android:name="android.app.arguments" android:value="-service"/>
    <!-- Application arguments -->
    <!-- ... -->
</service>

Then make sure you set the same android.app.lib_name metadata for both service(s) & activity elements:

<service ... >
    <!-- ... -->
    <meta-data android:name="android.app.lib_name"
                android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
    <!-- ... -->
</service>

I recommend you to use this method only if your activity and your service(s) share a large piece of code.

Separate .so files for app & service(s)

The second option is to create separate .so files for your app & service(s). First you need to create a separate server .pro file(s):

TEMPLATE = lib
TARGET = server
CONFIG += dll
QT += core
SOURCES += \
    server.cpp

The server .so main entry is the main function:

#include <QDebug>

int main(int argc, char *argv[])
{
    qDebug() << "Hello from service";
    return 0
}

Last you need to load the server .so file:

<service ... >
    <!-- ... -->
    <meta-data android:name="android.app.lib_name" android:value="server"/>
    <!-- ... -->
</service>

Use QtRemoteObject for communication

We’ve seen how to create and how to start a Qt on Android service, now let’s see how to do the communication between them. There are lots of solutions out there, but for any Qt project, I do recommend you use QtRemoteObject, because it will make your life so easy!

QtRemoteObjects is a playground Qt module led by Ford, for object remoting between processes/devices:

  • exports QObjects remotely (properties, signals & slots)
  • exports QAbstractItemModels remotely
  • creates a replicant on the client side you can interface with
  • repc generates source & replica (server & client) source files from .rep files
    • .rep file is the QtRemoteObjects IDL (interface description language)

As you can see it’s very Qt specific!
Let’s see how to add it to your projects and use it.

Get QtRemoteObjects

QtRemoteObjects project is located at http://code.qt.io/cgit/playground/qtremoteobjects.git/, to get it you need to run the following commands:

$ git clone git://code.qt.io/playground/qtremoteobjects.git
$ cd qtremoteobjects
$ ~/Qt/5.7/android_armv7/bin/qmake -r && make && make install

If needed, replace ~/Qt/5.7/android_armv7 with your Qt version and android ABI of choice.

Use QtRemoteObjects

Using QtRemoteObjects is pretty easy, you need to do a few easy steps:

– add QtRemoteObjects to your .pro files

# ...
QT += remoteobjects
# ...

– create .rep file(s)

class PingPong {
    SLOT(void ping(const QString &msg));
    SIGNAL(pong(const QString &msg));
}

– add .rep file(s) to the server .pro file

# ...
REPC_SOURCE += pingpong.rep
# ...

– add .rep file(s) to the client .pro file

# ...
REPC_REPLICA += pingpong.rep
# ...

– QtRemoteObjects source(server) side implementation

#include <QCoreApplication>
#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[])
{
    QCoreApplication app(argc, argv);

    QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica")));
    PingPong pingPongServer;
    srcNode.enableRemoting(&pingPongServer);

    return app.exec();
}

Let’s check the code a little bit.
First you need to implement all .rep interfaces (PingPongSource), then export PingPong object using enableRemoting.

– QtRemoteObjects replica(client) side implementation

#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");
// ....

Let’s check the code:

  • use QRemoteObjectNode to connect to QRemoteObjectHost
  • use QRemoteObjectNode:acquire to link the local object to the remote one
  • use the acquired object as its local (call slots, connect to signals, etc.)

As you can see, using Qt + QtRemoteObject is (much?) easier and more straight forward than Android’s Java services + AIDL 😉

Limitations

  • the activities & service(s) must run on a different process.
  • it is not possible (yet) to use QtCreator to easily add a service section to your AndroidManifest.xml file check QTCREATORBUG-16884
  • it is not possible (yet) to use QtCreator to easily generate a service subproject for us, check QTCREATORBUG-16885
  • it is not possible (yet) to see the services logs in QtCreator. You’ll need to use
     $ adb logcat 

    to see it, check QTCREATORBUG-16887

  • it is not possible (yet (hopefully)) to debug the services in QtCreator. This feature will take some time to implement it, therefore I’ll not hold my breath for it, check QTCREATORBUG-16886

Please use the above bug report links to vote for your favorite tasks, the ones that have more votes (usually) are implemented first!

You can find the full source code of this article here: https://github.com/KDAB/android

9 thoughts on “Qt on Android: How to create an Android service using Qt”

  1. Impressive!! Thanks Bogdan
    I didn’t know the QtRemoteObjects exitence, that will simplify the process a lot.

  2. Thanks for article!
    I create my own project with separate .so files for app & service similarly with your client/server example. It works nice, but I encountered some problems.

    My sequencing:
    1) client start service calling static java method with ‘startService’ command.
    2) service run server.so lib with c++ code (specified in android.app.lib_name).
    3) client and server exchange some information.
    4) client stop service calling static java method with ‘stopService’ command.

    My problems:
    1) after ‘stopService’ command java part stopped, but c++ part continues execute (I see that client/server exchange does not stopped). It’s normal behavior? Exist some simple method stop service from client fully?
    2) client subproject has access to java code and it’s work nice. But how I can access to java world from server subproject? I’m trying to add in server.pro file next code:
    QT += androidextras
    ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
    DISTFILES += android/src/MyService.java
    but this java file not building and not available from c++ code.

    1. OK, for these days I found some working solutions for my troubles:
      1) When java part destroyed, I call c++ quit method with jni and thus stop c++ part.
      2) On Ubuntu I add java files to service subproject with this commands:
      ANDROID_JAVA_SOURCES.path = java/package/path
      ANDROID_JAVA_SOURCES.files = $$files(src/*.java)
      INSTALLS += ANDROID_JAVA_SOURCES
      unfortunately, on Windows with mingw this commands cause an error.

Leave a Reply

Your email address will not be published. Required fields are marked *

By continuing to use the site, you agree to the use of cookies. More information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close