|
| 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 | +*/ |
0 commit comments