From 509f4d23de0a7bfe320cf334d565a4b50cdc61ae Mon Sep 17 00:00:00 2001 From: cbullinger Date: Fri, 28 Jul 2023 11:45:18 -0400 Subject: [PATCH] Initial draft --- source/sdk/kotlin/sync.txt | 1 + source/sdk/kotlin/sync/background-sync.txt | 162 +++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 source/sdk/kotlin/sync/background-sync.txt diff --git a/source/sdk/kotlin/sync.txt b/source/sdk/kotlin/sync.txt index e96856daa79..e1b79cec5e2 100644 --- a/source/sdk/kotlin/sync.txt +++ b/source/sdk/kotlin/sync.txt @@ -14,6 +14,7 @@ Device Sync - Kotlin SDK Handle Sync Errors Set the Client Log Level Stream Data to Atlas + Sync Data in the Background Partition-Based Sync .. contents:: On this page diff --git a/source/sdk/kotlin/sync/background-sync.txt b/source/sdk/kotlin/sync/background-sync.txt new file mode 100644 index 00000000000..9d93847fb41 --- /dev/null +++ b/source/sdk/kotlin/sync/background-sync.txt @@ -0,0 +1,162 @@ +.. _kotlin-background-sync: + +======================================== +Sync Data in the Background - Kotlin SDK +======================================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + + +If you need to sync data when your app isn't running, you can sync realms +in a background process. + +Prerequisites +------------- + +To get started with background synchronization, you need to add the +following dependencies to your Android application: + +- :android:`androidx.work:work-runtime ` + to enqueue jobs +- :android:`androidx.concurrent:concurrent-futures ` + to return job results from a background worker + +Example +------- + +Background sync requires two things: + +- Synchronization logic +- A scheduled job that periodically performs the sync logic + +Synchronization Logic +~~~~~~~~~~~~~~~~~~~~~ + +First, write the custom logic that synchronizes your realm. Treat this +logic as a standalone connection to your backend. As a result, you'll +need to: + +- initialize the Realm SDK +- authenticate a user to open the realm + +You can use a user's cached credentials if the user recently used the app. + +Open the realm, then use `SyncSession.downloadAllServerChanges() <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb.sync/-sync-session/download-all-server-changes.html>`__ +and `SyncSession.uploadAllLocalChanges() <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb.sync/-sync-session/upload-all-local-changes.html>`__ +to synchronize the realm fully with the backend. For more information, see +:ref:`Manage Sync Sessions `. + +You can execute this logic as a background process using a subclass of +:android:`CoroutineWorker `. +Put your synchronization logic in the ``doWork()`` method of your worker: + +.. code-block:: kotlin + :caption: Example RealmBackgroundWorker.kt + + package com.mongodb.app.worker + + import android.annotation.SuppressLint + import android.content.Context + import androidx.concurrent.futures.ResolvableFuture + import androidx.work.CoroutineWorker + import androidx.work.WorkerParameters + import com.mongodb.app.app + import com.mongodb.app.data.RealmSyncRepository + import io.realm.kotlin.Realm + import io.realm.kotlin.mongodb.syncSession + + class RealmBackgroundWorker(context: Context, workerParams: WorkerParameters) : + CoroutineWorker(context, workerParams) { + + private lateinit var future: ResolvableFuture + @SuppressLint("RestrictedApi") + override suspend fun doWork(): Result { + future = ResolvableFuture.create() + + // Get the realm configuration + val syncRepository = RealmSyncRepository { session, error -> + future.setException(error) + } + val config = syncRepository.getRealmConfiguration() + + // Check if user is logged-in + if (app.currentUser?.loggedIn == true) { + val realm = Realm.open(config) + try { + realm.syncSession.downloadAllServerChanges() + realm.syncSession.uploadAllLocalChanges() + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + return future.get() + } + companion object { + const val UNIQUE_WORK_NAME = "RealmBackgroundWorker" + } + } + + +Worker +~~~~~~ + +To create a worker that periodically performs background sync: + +1. Create a set of :android:`Constraints ` + that specify the conditions required for your worker. Because synchronizing + a realm uses data, you should consider only downloading changes in the + background when the device is *not*: + + - low on battery + - using a metered data source + +2. Specify how frequently your worker should execute. Your repeat interval + depends on how frequently data updates in the realm and how often users + open your application. + + - If the realm frequently updates throughout the day, consider setting a repeat + interval of 1-3 hours. + - If the realm only updates a small number of times each day, it's best to + set a higher repeat interval and only background sync once or twice a day. + +3. Enqueue your worker with the Android OS. Assign it a unique identifier + so that you can update the job in the future. + +.. tip:: + + You can create the background sync job inside an Application subclass in + your app to guarantee that the logic only executes once every time your + application runs. + + +.. code-block:: kotlin + :caption: Example worker + + // Create constraints that require a network connection and + // that the device is not low on battery + val constraints: Constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.UNMETERED) + .setRequiresBatteryNotLow(true) + .build() + + val backgroundRealmSync = + PeriodicWorkRequestBuilder( + // Repeat every 12 hours + 12, TimeUnit.HOURS, + // Execute job at any point during that 12-hour period + 12, TimeUnit.HOURS + ) + .setConstraints(constraints) + .build() + + // Enqueue the work job, replacing it with the most recent + // version if we update it + WorkManager.getInstance(this).enqueueUniquePeriodicWork( + RealmBackgroundWorker.UNIQUE_WORK_NAME, + ExistingPeriodicWorkPolicy.UPDATE, + backgroundRealmSync + )