Skip to content
This repository was archived by the owner on Mar 4, 2023. It is now read-only.

Commit 6457c89

Browse files
committed
added droid sync service guide
1 parent dda366b commit 6457c89

File tree

8 files changed

+191
-8
lines changed

8 files changed

+191
-8
lines changed

doc/androidbackgroundservice.dox

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ without much effort. However, a few additional setup steps are needed. This clas
66
the QtService implementation of an android service to add the synchronization aspect.
77

88
@warning This class alone is **not** sufficient to make synchronization possible. Have a look at
9-
the @ref android_sync_guide to learn how to add background synchronization to your project.
9+
the @ref qtdatasync_sync_guide_android to learn how to add background synchronization to your project.
1010

1111
@sa AndroidSyncControl, QtService::Service, @ref qtservice_backends_android
1212
*/
@@ -120,7 +120,7 @@ want to run must show a foreground notification while running in order to not be
120120
system. A basic sample could be the following.
121121

122122
The code to create the notification was first written in java:
123-
@snippet SvcHelper.java create_notification
123+
@snippet android/src/de/skycoder42/qtdatasync/sample/androidsync/SvcHelper.java java_create_notification
124124
And that method is then simply called from the implementation:
125125
@snippet syncservice.cpp jni_create_notification
126126

doc/androidsynccontrol.dox

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ used.
88

99
@warning This class alone is **not** sufficient to make this possible. You will also have to
1010
create a service based on AndroidBackgroundService and correctly set up the Manifest and other
11-
things. Have a look at the @ref android_sync_guide to learn how to add background synchronization
11+
things. Have a look at the @ref qtdatasync_sync_guide_android to learn how to add background synchronization
1212
to your project.
1313

14-
@sa AndroidBackgroundService, @ref android_sync_guide, IosSyncDelegate
14+
@sa AndroidBackgroundService, @ref qtdatasync_sync_guide_android, IosSyncDelegate
1515
*/
1616

1717
/*!

doc/backsync.dox

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*!
2+
@page qtdatasync_sync_guide Adding background synchronization
3+
@brief A Guide for adding background synchronization to mobile applications
4+
5+
This page consists of two guides, one for android and one for iOs, to add background
6+
synchronization to an application. There are multiple steps required that differ for
7+
each platform and those are discussed here.
8+
9+
@tableofcontents
10+
11+
@section qtdatasync_sync_guide_android Android Background synchronization
12+
For Android, you will have to add an additional service to your application that
13+
is to be run in the background, independently from your main application,
14+
to perform the sync. This library already supports all the elements needed to do so,
15+
but you still need to setup the project yourself.
16+
17+
You can find a working example of this tutorial in the repository. Check out the
18+
[AndroidSync Sample](https://github.com/Skycoder42/QtDataSync/tree/master/examples/datasync/AndroidSync).
19+
20+
@note For the Android variant, you will have to install
21+
[QtService](https://github.com/Skycoder42/QtService) as well.
22+
23+
@sa QtDataSync::AndroidBackgroundService, QtDataSync::AndroidSyncControl
24+
25+
@subsection qtdatasync_sync_guide_android_base Prepare the project
26+
@note For this section it is assumed you are using QtCreator and a qmake based project.
27+
For anything else you will have to figure out yourself how to do steps equivalent
28+
to these for your build system.
29+
30+
First create any normal project in QtCreator. Select an android kit to build it and
31+
then go to "Projects > Build > Build Android APK" and under "Android" press
32+
"Create Templates". This should bring up a dialog to add android files to your project.
33+
Follow that wizard to add the files. Your pro file should now contain `ANDROID_PACKAGE_SOURCE_DIR`.
34+
35+
Next you have to add the correct Qt modules. These are: `QT += datasyncandroid`.
36+
Build and run your project. It should still function normally
37+
38+
@subsection qtdatasync_sync_guide_android_service Add the synchronization service to C++
39+
The next step is to add the actual service to your project that performs the sync. This is done
40+
in 3 steps:
41+
42+
@subsubsection qtdatasync_sync_guide_android_service_cpp Implement the service interface
43+
Add a new C++ class to the project and let it inherit from QtDataSync::AndroidBackgroundService.
44+
Adjust the constructor to match the one of the parent class (beware the reference) and implement
45+
any methods you need to. The only required method to be implemented is
46+
QtDataSync::AndroidBackgroundService::createForegroundNotification. An example would be:
47+
@snippet syncservice.h droid_service_hdr
48+
49+
And the corresponding implementation:
50+
@snippet syncservice.cpp droid_service_impl
51+
52+
@note The foreground notification is needed as an ongoing notification to be shown while the service
53+
is running. This is required by Android. For a basic example of how to create the notification see
54+
the QtDataSync::AndroidBackgroundService::createForegroundNotification documentation.
55+
56+
@subsubsection qtdatasync_sync_guide_android_service_main Prepare the main to run the service
57+
After creating the sync service class, you need to add code to your main to differentiate between
58+
whether the application is started normally or as a service.
59+
60+
The general idea is to split your main into 3 functions. One named `activityMain` which does the
61+
stuff you did before, i.e. create a gui, an `serviceMain` that launces the service and the actual
62+
`main` that now only decides which of the other two to call based on the start parameters.
63+
64+
The `serviceMain` should simply instanciate and execute the service from the given parameters:
65+
@snippet AndroidSync/main.cpp service_main
66+
67+
The actual `main` has to scan the passed arguments for the `--backend` argument. If found, the
68+
`serviceMain` should be run, otherwise the `activityMain`:
69+
@snippet AndroidSync/main.cpp actual_main
70+
71+
Again, the `activityMain` as basically what was previously the normal `main`, just moved out into
72+
a different function.
73+
74+
@note It is also possible to create two seperate binaries and reference them. But for the sake of
75+
simplicity, only the simple method with one binary is shown here.
76+
77+
@subsection qtdatasync_sync_guide_android_manifest Update the AndroidManifest.xml
78+
After the C++ code is now ready, the final step is to update the AndroidManifest.xml. We need to
79+
add a few elements to register all components and permissions that are needed to actually run the
80+
service.
81+
82+
@subsubsection qtdatasync_sync_guide_android_manifest_service Add the service
83+
Now that the service was created and is startable if the correct command line arguments are
84+
specified, we have to add it to the manifest to make it known to the
85+
android system so it can actually be started. The `<service>` should be placed next to the
86+
`<activity>` element in the manifest, as a child of the root `<application>` element. The following
87+
shows an example for such a service definition:
88+
89+
@snippet android/AndroidManifest.xml manifest_service
90+
91+
Most of the service was just copied from the `<activity>` element. The attributes and elements that
92+
are different from the activity and thus must be changed after copying it over are:
93+
- `android:process=":service_process"` - Makes shure the service runs in a seperate process
94+
- `android:name="de.skycoder42.qtservice.AndroidService"` - Specify the java class to be used
95+
- `<meta-data android:name="android.app.arguments" android:value="--backend android"/>` - Pass the arguments to the `main`
96+
- `<meta-data android:name="android.app.background_running" android:value="true"/>` - Allow background execution
97+
98+
@subsubsection qtdatasync_sync_guide_android_manifest_startup Add the startup receiver
99+
Now that the service has been added, we need one more component to be added to the manifest. This
100+
is the boot receiver, which is started by android after a reboot and is needed to reactive your
101+
service after a boot. The class is ready to use, so you only need to add the following to the
102+
manifest, right after the `<service>` element (still inside the `<application>` element):
103+
104+
@snippet android/AndroidManifest.xml manifest_receiver
105+
106+
@subsubsection qtdatasync_sync_guide_android_manifest_permissions Add additional permissions
107+
The last thing to add to the manifest are additional permissions. We need permissions to start
108+
a foreground service (which is exactly what we are doing) and allow the app to receive a signal
109+
when the system was rebooted, which is needed to reactive the service after a reboot. Simply
110+
add the following elements after the `%%INSERT_PERMISSIONS` comment
111+
112+
@snippet android/AndroidManifest.xml manifest_permissions
113+
114+
@subsection qtdatasync_sync_guide_android_controller Use the controller to enable sync
115+
With all the previous steps the service is fully prepared to be used. All thats left to do is to
116+
actually use it by enabeling background synchronization. You can do this with the
117+
QtDataSync::AndroidSyncControl class. In the example, it is used from QML you you can play around
118+
with the possibilities, but for the sake of this guide, the simplest way is to just statically
119+
enable it from your `activityMain`:
120+
121+
@code
122+
int activityMain(int argc, char *argv[])
123+
{
124+
QGuiApplication app(argc, argv);
125+
126+
QtDataSync::AndroidSyncControl control;
127+
// control.setInterval(...);
128+
control.setEnabled(true);
129+
qDebug() << "control.enabled" << control.isEnabled();
130+
131+
//...
132+
}
133+
@endcode
134+
135+
@attention Do not forget to register the notification channel for the foreground notification
136+
used by the service from your activity (java code below):
137+
138+
@snippet android/src/de/skycoder42/qtdatasync/sample/androidsync/SvcHelper.java java_create_channel
139+
140+
@subsection qtdatasync_sync_guide_android_test Testing the setup
141+
With that the preperations are done and ready to test. Compile and run your application and
142+
check if you can find `control.enabled true` in the log. You can also run
143+
@code
144+
adb shell dumpsys alarm | grep -B 1 -A 3 de.skycoder42.qtdatasync.backgroundsync
145+
@endcode
146+
to check if the alarm was correctly registered and when synchronization is triggered the next time.
147+
Wait for the timeout to arrive and see if the service executes correctly (i.e. without crashing).
148+
You can consult the logs via
149+
@code
150+
adb logcat
151+
@endcode
152+
to check the output of the service. You should find a message like:
153+
@code
154+
[[SYNCSERVICE]] Sync completed in state: <some state>
155+
@endcode
156+
157+
If thats the case, the service works correctly, and you can extend it as you please.
158+
*/

examples/datasync/AndroidSync/android/AndroidManifest.xml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@
6161
<!-- extract android style -->
6262
</activity>
6363

64-
<service android:process=":test_service" android:name="de.skycoder42.qtservice.AndroidService">
64+
<!--! [manifest_service] -->
65+
<service android:process=":service_process" android:name="de.skycoder42.qtservice.AndroidService">
6566
<!-- Application to launch -->
6667
<meta-data android:name="android.app.arguments" android:value="--backend android"/>
6768
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
@@ -87,12 +88,15 @@
8788
<!-- Background running -->
8889
<meta-data android:name="android.app.background_running" android:value="true"/>
8990
</service>
91+
<!--! [manifest_service] -->
9092

93+
<!--! [manifest_receiver] -->
9194
<receiver android:name="de.skycoder42.qtdatasync.SyncBootReceiver">
9295
<intent-filter>
9396
<action android:name="android.intent.action.BOOT_COMPLETED"/>
9497
</intent-filter>
9598
</receiver>
99+
<!--! [manifest_receiver] -->
96100

97101
</application>
98102

@@ -102,8 +106,10 @@
102106
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
103107
Remove the comment if you do not require these default permissions. -->
104108
<!-- %%INSERT_PERMISSIONS -->
105-
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
109+
<!--! [manifest_permissions] -->
106110
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
111+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
112+
<!--! [manifest_permissions] -->
107113

108114
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
109115
Remove the comment if you do not require these default features. -->

examples/datasync/AndroidSync/android/src/de/skycoder42/qtdatasync/sample/androidsync/SvcHelper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
public class SvcHelper {
1616
private static final String ForegroundChannelId = "de.skycoder42.qtdatasync.sample.androidsync.notify";
1717

18-
//! [create_notification]
18+
//! [java_create_notification]
1919
public static Notification createFgNotification(Context context) {
2020
return new NotificationCompat.Builder(context, ForegroundChannelId)
2121
.setContentTitle("AndroidSync Synchronization Service")
@@ -28,8 +28,9 @@ public static Notification createFgNotification(Context context) {
2828
.setCategory(NotificationCompat.CATEGORY_SERVICE)
2929
.build();
3030
}
31-
//! [create_notification]
31+
//! [java_create_notification]
3232

33+
//! [java_create_channel]
3334
public static void registerNotificationChannel(Context context) {
3435
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
3536
return;
@@ -46,4 +47,5 @@ public static void registerNotificationChannel(Context context) {
4647
foreground.setShowBadge(false);
4748
manager.createNotificationChannel(foreground);
4849
}
50+
//! [java_create_channel]
4951
}

examples/datasync/AndroidSync/main.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace {
88

9+
//! [activity_main]
910
int activityMain(int argc, char *argv[])
1011
{
1112
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
@@ -23,15 +24,19 @@ int activityMain(int argc, char *argv[])
2324

2425
return app.exec();
2526
}
27+
//! [activity_main]
2628

29+
//! [service_main]
2730
int serviceMain(int argc, char *argv[])
2831
{
2932
SyncService service{argc, argv};
3033
return service.exec();
3134
}
35+
//! [service_main]
3236

3337
}
3438

39+
//! [actual_main]
3540
int main(int argc, char *argv[])
3641
{
3742
for(auto i = 0; i < argc; i++) {
@@ -40,4 +45,5 @@ int main(int argc, char *argv[])
4045
}
4146
return activityMain(argc, argv);
4247
}
48+
//! [actual_main]
4349

examples/datasync/AndroidSync/syncservice.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
#include "syncservice.h"
22
#include <QtAndroid>
33

4+
//! [droid_service_impl]
45
SyncService::SyncService(int &argc, char **argv) :
56
AndroidBackgroundService{argc, argv}
67
{}
78

9+
void SyncService::prepareSetup(QtDataSync::Setup &setup)
10+
{
11+
// prepare the setup here, i.e.
12+
// setup.setRemoteConfiguration(QtDataSync::RemoteConfig{ ... ]);
13+
}
14+
815
void SyncService::onSyncCompleted(QtDataSync::SyncManager::SyncState state)
916
{
1017
qDebug() << "[[SYNCSERVICE]]" << "Sync completed in state:" << state;
1118
exitAfterSync();
1219
}
20+
//! [droid_service_impl]
1321

1422
//! [jni_create_notification]
1523
QAndroidJniObject SyncService::createForegroundNotification()

examples/datasync/AndroidSync/syncservice.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <QtDataSyncAndroid/AndroidBackgroundService>
55

6+
//! [droid_service_hdr]
67
class SyncService : public QtDataSync::AndroidBackgroundService
78
{
89
Q_OBJECT
@@ -11,8 +12,10 @@ class SyncService : public QtDataSync::AndroidBackgroundService
1112
explicit SyncService(int &argc, char **argv);
1213

1314
protected:
15+
void prepareSetup(QtDataSync::Setup &setup) override;
1416
void onSyncCompleted(QtDataSync::SyncManager::SyncState state) override;
1517
QAndroidJniObject createForegroundNotification() override;
1618
};
19+
//! [droid_service_hdr]
1720

1821
#endif // SYNCSERVICE_H

0 commit comments

Comments
 (0)