Android Service with Qt Remote Objects

Demonstrates how to run an Android service in a separate process, and how to communicate between the service process and the main process using Qt Remote Objects.

This example demonstrates how to create and run an Android service in a separate process from the main application process, and then exchange data between QML/C++ and the Java service using Qt Remote Objects.

When clicking the Send to Service button, the name entered in the QML view, Qt, in this case, is sent to the Android service. Then, the service replies back with a message Hello Qt which is printed in the QML view.

Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.

Create the Service

To start a service in its own process, extend the QtService class for your service. Extending QtService allows the service to load the necessary Qt libraries used for Qt, like Qt Remote Objects libraries.

Start by creating the Java service class. The following class extends QtService and acts as your service entry point:

package org.qtproject.example.qtandroidservice;

import android.content.Context;
import android.content.Intent;
import org.qtproject.qt5.android.bindings.QtService;
import android.util.Log;

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");
    }
}

This class can have any logic you want using Java code. However, you don't need any logic to communicate with Qt as that will be done using Qt Remote Objects.

Manage the AndroidManifest.xml File

To use the service, it must be declared in the AndroidManifest.xml file as follows:

    <service android:process=":qt_service" android:name=".QtAndroidService">

            <meta-data android:name="android.app.lib_name" android:value="service"/>
            <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%% --"/>

            <!-- 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_resource_id" android:resource="@array/load_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 -->

            <!-- Background running -->
            <meta-data android:name="android.app.background_running" android:value="true"/>
            <!-- Background running -->
        </service>

The important part of this service declaration is the lib_name part. It will ensure that the service is run by the service's own lib file:

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

Handle the Application Start

To start the service, call the following function from the application's main():

void startService()
{
    QAndroidIntent serviceIntent(QtAndroid::androidActivity().object(),
                                        "org/qtproject/example/qtandroidservice/QtAndroidService");
    QAndroidJniObject result = QtAndroid::androidActivity().callObjectMethod(
        "startService",
        "(Landroid/content/Intent;)Landroid/content/ComponentName;",
        serviceIntent.handle().object());
}

Handle the Service Start

QAndroidService is used to create the service process. For the service to start from its own .so lib file, create a sub-project with the following .pro file:

TEMPLATE = lib
TARGET = service
CONFIG += dll

Communication with Qt Remote Objects

To use Qt Remote Objects, define a .rep file:

class QtAndroidService {
    SLOT(void sendToService(const QString &name));
    SIGNAL(messageFromService(const QString &message));
}

Define the .rep source file in the service project .pro:

REPC_SOURCE += ../common/qtandroidservice.rep

And in the application's .pro file:

REPC_REPLICA += ../common/qtandroidservice.rep

Then define the class used by Qt Remote Objects, which has a sendToService() slot and a messageFromService() signal:

#include "rep_qtandroidservice_source.h"

class QtAndroidService : public QtAndroidServiceSource
{
public slots:
    void sendToService(const QString &name) override {
        emit messageFromService("Hello " + name);
    };
};

Include the class in the previous snippet in main.cpp:

#include "rep_qtandroidservice_replica.h"

Now, in the service's service_main.cpp, create the QRemoteObjectHost node and start QAndroidService:

#include "qtandroidservice_ro.h"

#include <QDebug>
#include <QAndroidService>

int main(int argc, char *argv[])
{
    qWarning() << "QtAndroidService starting from separate .so";
    QAndroidService app(argc, argv);

    QRemoteObjectHost srcNode(QUrl(QStringLiteral("local:replica")));
    QtAndroidService qtAndroidService;
    srcNode.enableRemoting(&qtAndroidService);

    return app.exec();
}

In the application's main.cpp, create the client node and connect it to the source node created in the service and connect it to the QML view:

    QRemoteObjectNode repNode;
    repNode.connectToNode(QUrl(QStringLiteral("local:replica")));
    QSharedPointer<QtAndroidServiceReplica> rep(repNode.acquire<QtAndroidServiceReplica>());
    engine.rootContext()->setContextProperty("qtAndroidService", rep.data());
    bool res = rep->waitForSource();
    Q_ASSERT(res);
    rep->sendToService("Qt");

Then, add a Connections element to watch for the incoming messages from the service:

    Connections {
        target: qtAndroidService
        function onMessageFromService(message) {
            pongText.text = message
        }
    }

And set the onClicked for the sending button to:

        onClicked: qtAndroidService.sendToService(pingText.text)

Example project @ code.qt.io

See also Android Service with Qt Remote Objects - Same Lib File, Android Services, Qt for Android, and Qt Android Extras.

© 2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.