summaryrefslogtreecommitdiff
path: root/doc/src/platforms/android/android-services.qdoc
blob: bcaba1556edeb758df90d2d8244ad23a77d8fa23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
\page android-services.html
\title Android Services
\brief Provides information about Android Services support in Qt.

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.

\section1 Assemble the Service

To get started, create an Android package directory as instructed in
\l{Qt Creator: Deploying Applications to Android Devices}. This directory
contains the \c AndroidManifest.xml file. Inside the package directory,
create a \c src directory, where all your Java packages and classes
will be created.

\section2 Create the Service Class

You can create a service by extending the class \c QtService or
\l{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 \c QtService or \c Service. Let's start with a simple service,
as follows:

\code
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.qtproject.qt5.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;
    }
}
\endcode

\section2 Start the Service

Android allows starting services on demand or at boot time. You can do both using
Qt as well.

\section3 Start a Service On Demand

You can start the service in the following ways:

\list
    \li Directly from C++ using \l {QtAndroidExtras}{QAndroidIntent} and
        \l {QtAndroidExtras}{QAndroidJniObject}, by creating a service
        \l {Android: Intent}{Intent} and calling the app's main activity method
        \l {Android: startService()}{startService()}:

        \code
        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());
        \endcode

    \li Start the service by calling a Java method. The easiest way is to create
        a static method in your service class:

        \code
        public static void startQtAndroidService(Context context) {
                context.startService(new Intent(context, QtAndroidService.class));
        }
        \endcode

        The you can call it from C++ using the following JNI call:

        \code
        QAndroidJniObject::callStaticMethod<void>(
            "org/qtproject/example/qtandroidservice/QtAndroidService",
            "startQtAndroidService",
            "(Landroid/content/Context;)V",
            QtAndroid::androidActivity().object());
        \endcode
\endlist

\section3 Start a Service At Boot Time

To run a service at boot time, you need a \l{Android: BroadcastReceiver}
{BroadcastReceiver}.

Create a custom Java class:

\code
public class QtBootServiceBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent startServiceIntent = new Intent(context, QtAndroidService.class);
        context.startService(startServiceIntent);
    }
}
\endcode

Add the following \c uses-permission in the body of the \c <manifest> section in
the \c AndroidManifest.xml file:

\badcode
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
\endcode

Also, add the \c receiver definition in the body of the \c <application> section:

\badcode
<receiver android:name=".QtBootServiceBroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
\endcode

\note Android 8.0 introduced some limitations on running background services,
which means using a nomal \c Service class might not work. For more information,
see Android's recommendation to use either \c {Android: Foreground services}{Foreground services}
or \l {Android: JobIntentService}{JobIntentService}.

\section2 Manage the Service in AndroidMnifest.xml

For the service to be usable in an Android app, you must declare it in the
\c AndroidManifest.xml file. Let's start with adding the service section:

\list
    \li When extending \c Service, just declare the service section as a normal Android
        service. Add the following inside the \c <application> section:

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

        This way the service will start in the same process as \c 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:

        \badcode
        android:process=":qt_service"
        \endcode

    \target Extending QtActivity AndroidManifest.xml
    \li When extending \c QtService, you need to declare other items for loading
        all the necessary libs required for Qt, mainly the same items as in
        \c <activity> section for \c QtActivity. Add the following:

        \badcode
        <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>
        \endcode
\endlist

\note Make sure to define the following to run the service in the background:

\badcode
<meta-data android:name="android.app.background_running" android:value="true"/>
\endcode

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.

\section3 Service in the Same Process as QtActivity

To run a service in the same process as QtActivity, declare the service header
as follows:

\badcode
<service android:name=".QtAndroidService">
\endcode

\section3 Service in Separate Process

To run a service in a dedicated process, declare the service header as follows:

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

Qt loads the \c .so file defined in \c android.app.lib_name \c meta-data,
and calls the \c main() function with all the arguments set in
\c android.app.arguments \c 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.

\section4 Use the Same .so Lib File

Using the same \c .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 \c main() function according the arguments provided.
Add the following argument declaration to your service body:

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

Then make sure the service \c android.app.lib_name is the same as
the main activity, add the following:

\badcode
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
\endcode

When using the same \c .so lib file, your application's \c 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:

\code
if (argc <= 1) {
    // code to handle main activity execution
} else if (argc > 1 && strcmp(argv[1], "-service") == 0) {
    qDebug() << "Service starting with from the same .so file";
    QAndroidService app(argc, argv);
    return app.exec();
} else {
    qWarning() << "Unrecognized command line argument";
    return -1;
}
\endcode

\section4 Use a Separate .so Lib File

In this case, you need to have a sub-project with a \c lib template that
provides a different executable for the service. A sample project \c .pro is:

\badcode
TEMPLATE = lib
TARGET = service
CONFIG += dll
QT += core androidextras

SOURCES += \
    service_main.cpp

HEADERS += servicemessenger.h
\endcode

In the \c service_main.cpp you could have the following:

\code
#include <QDebug>
#include <QAndroidService>

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

    return app.exec();
}
\endcode

Define the \c android.app.lib_name for the service in the \c AndroidManifest.xml:

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

\section1 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.

\section2 Native C++ Calls from Java Service

This can work with services running in the same process as \c QtActivity and even
if \c Service is extended. For more information, see
\l{Calling QML/C++ Functions from Java Code}.

\section2 Using Android BroadcastReceiver

\l {Android: BroadcastReceiver}{Android BroadcastReceiver} enables exchanging
messages between the Android system, apps, activities and services. Similarly
to other Android features, Qt can use broadcast receivers to exchange messages
between \c QtActivity and your service. Let's start with logic to send a message
from your service. Add the following in your service implementation, which calls
\l{Android: sendBroadcast()}{sendBroadcast()}:

\code
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    int ret = super.onStartCommand(intent, flags, startId);

    Intent sendToUiIntent = new Intent();
    sendToUiIntent.setAction(ActivityUtils.BROADCAST_CUSTOM_ACTION);
    sendToUiIntent.putExtra("message", "simple_string");

    Log.i(TAG, "Service sending broadcast");
    sendBroadcast(sendToUiIntent);

    return ret;
}
\endcode

Then, you need to create and register the broadcast receiver from
the Qt's main activity. The easiest way is to create a custom class with a method
and implement all that logic in Java. In the following example, the service sends
a message \c "simple_string" to Qt by calling the native method \c sendToQt():

\code
public class ServiceBroadcastUtils {

    private static native void sendToQt(String message);

    private static final String TAG = "ActivityUtils";
    public static final String BROADCAST_CUSTOM_ACTION = "org.qtproject.example.qtandroidservice.broadcast.custom";

    public void registerServiceBroadcastReceiver(Context context) {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BROADCAST_CUSTOM_ACTION);
        context.registerReceiver(serviceMessageReceiver, intentFilter);
        Log.i(TAG, "Registered broadcast receiver");
    }

    private BroadcastReceiver serviceMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "In OnReceive()");
            if (BROADCAST_CUSTOM_ACTION.equals(intent.getAction())) {
                String message = intent.getStringExtra("message");
                sendToQt(data);
                Log.i(TAG, "Service sent back message to C++: " + message);
            }
        }
    };
}
\endcode

For more information on working with native calls, see
\l{Calling QML/C++ Functions from Java Code}.

To make use of all that, start your service as shown in \l{Start the Service},
an then register the broadcast receiver by calling the method
\c registerServiceBroadcastReceiver():

\code
QAndroidJniEnvironment env;
jclass javaClass = env.findClass("org/qtproject/example/qtandroidservice/ActivityUtils");
QAndroidJniObject classObject(javaClass);

classObject.callMethod<void>("registerServiceBroadcastReceiver",
                             "(Landroid/content/Context;)V",
                             QtAndroid::androidContext().object());
\endcode

\section2 Using Qt Remote Objects

\l{Getting Started with Qt Remote Objects}{Qt Remote Objects} offers an easy way
to share APIs between Qt processes. The main concept is to server in the service
process, and have a replica in the Qt application, then those two parts are able
to exchange data between each other, using signals and slots.

\section3 Prepare the replica
Let's consider a service example with separate \c .so lib file. Define a
\c .rep file which defines our communication class:

\code
class ServiceMessenger {
    SLOT(void ping(const QString &message));
    SIGNAL(pong(const QString &message));
}
\endcode

The define the class in the service sub-project as \c servicemessenger.h:

\code
#include "rep_servicemessenger_source.h"

class ServiceMessenger : public ServiceMessengerSource {
public slots:
    void ping(const QString &name) override {
        emit pong("Hello " + name);
    }
};
\endcode

Then, add the \c .rep file to both the main application and service \c .pro files,
in the main application:

\badcode
QT += remoteobjects
REPC_REPLICA += servicemessenger.rep
\endcode

And in the service sub-project:

\badcode
QT += remoteobjects
REPC_SOURCE += servicemessenger.rep
\endcode

\section3 Connect the source and replica

Define the Qt Remote Objects source node in the service sub-project's \c main()
function:

\code
#include "servicemessenger.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")));
    ServiceMessenger serviceMessenger;
    srcNode.enableRemoting(&serviceMessenger);

    return app.exec();
}
\endcode

Then, in the application's \c main() function, connect to source node:

\code
QRemoteObjectNode repNode;
repNode.connectToNode(QUrl(QStringLiteral("local:replica")));
QSharedPointer<ServiceMessengerReplica> rep(repNode.acquire<ServiceMessengerReplica>());
bool res = rep->waitForSource();
Q_ASSERT(res);

QObject::connect(rep.data(), &ServiceMessengerReplica::pong, [](const QString &message){
    qDebug() << "Service sent: " << message;
});
rep->ping("Qt and Android are friends!");
\endcode

This example sends a message from the main application's process to the
service. The service replies with the same message, which is printed
on the debug logcat.

\note The same method could be used when using the same \c .so lib file. For more
information, see \l{Use the same .so Lib File}.

\section2 Using QAndroidBinder

While using \l{QtRemoteObjects}{Qt Remote Objects} for communication is a
cross-platform solution, \l QAndroidBinder Class Reference} is Android specific.
\l QAndroidBinder is a convenience class that implements the most important
methods in \l{Android: Binder}. It allows sending \l{QByteArray} or \l{QVariant}
objcets between processes.

\note 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 \l{QTBUG-78009}.

*/