From c80ca2f7778a6a394e54ed3faa8c9bd1c128815f Mon Sep 17 00:00:00 2001 From: cbullinger Date: Mon, 7 Aug 2023 11:53:47 -0400 Subject: [PATCH] Draft --- source/sdk/kotlin/sync.txt | 1 + source/sdk/kotlin/sync/background-sync.txt | 171 ++++++++++++++++++--- 2 files changed, 152 insertions(+), 20 deletions(-) diff --git a/source/sdk/kotlin/sync.txt b/source/sdk/kotlin/sync.txt index 9e9491d9e78..7b8e19de130 100644 --- a/source/sdk/kotlin/sync.txt +++ b/source/sdk/kotlin/sync.txt @@ -15,6 +15,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 index 77373b2aef7..9d93847fb41 100644 --- a/source/sdk/kotlin/sync/background-sync.txt +++ b/source/sdk/kotlin/sync/background-sync.txt @@ -1,9 +1,8 @@ -.. _swift-background-sync: -.. _ios-sync-changes-in-the-background: +.. _kotlin-background-sync: -======================================= -Sync Data in the Background - Swift SDK -======================================= +======================================== +Sync Data in the Background - Kotlin SDK +======================================== .. contents:: On this page :local: @@ -11,21 +10,153 @@ Sync Data in the Background - Swift SDK :depth: 2 :class: singlecol -Sync Changes in the Background ------------------------------- -If you want your app to update data in the background (while the app is -minimized), iOS requires you to implement :apple:`Background App Refresh -`. -Enabling Background App Refresh minimizes the time it takes for the user -to see the most recent data; without Background App Refresh, Realm -updates the data when the user launches the app, potentially resulting -in noticeable lag. +If you need to sync data when your app isn't running, you can sync realms +in a background process. -To use the realm while the device is locked, you must adjust the -file protection settings. See -:ref:`use-realm-when-the-device-is-locked`. +Prerequisites +------------- -For an example of how to sync changes in the background using the -SwiftUI ``BackgroundTask`` API, see: :ref:`Sync Data in the Background with -SwiftUI `. +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 + )