Navigation

Sync Changes Between Devices - Android SDK

Before you can access a synced realm from the client, you must:

  1. Enable sync in the Realm UI.
  2. Initialize the app
  3. Authenticate a user in your client project.

To open a synced realm, call getInstanceAsync(), passing in a a SyncConfiguration object. For example, the following code demonstrates how to create a realm with specific sync settings created using a SyncConfiguration object:

val config = SyncConfiguration.Builder(app.currentUser(), PARTITION)
.allowQueriesOnUiThread(true)
.allowWritesOnUiThread(true)
.build()
Realm.getInstanceAsync(config, object : Realm.Callback() {
override fun onSuccess(realm: Realm) {
Log.v("EXAMPLE", "Successfully opened a realm with reads and writes allowed on the UI thread.")
}
})

The code above shows how to open the realm asynchronously by using getInstanceAsync(). You can also open a realm synchronously by using getInstance(), but this may lead to temporary data inconsistencies while the remote data is downloaded, and is generally not recommended.

Note

To enable sync in your Android project, you need to configure it in the application-level build.gradle file by adding the following entry:

realm { syncEnabled = true }

For more information on enabling sync in Android, see Install Realm for Android.

The partition value specifies which subset of your data to sync. This is typically a user ID, project ID, store ID, or some other category identifier in your app that has particular relevance to the current user.

Tip
See also:

The syntax to read, write, and watch for changes on a synced realm is identical to the syntax for non-synced realms. While you work with local data, a background thread efficiently integrates, uploads, and downloads changesets.

Important
When Using Sync, Avoid Writes on the Main Thread

The fact that Realm performs sync integrations on a background thread means that if you write to your realm on the main thread, there's a small chance your UI could appear to hang as it waits for the background sync thread to finish a write transaction. Therefore, it's a best practice never to write on the main thread when using Realm Sync.

The following code reads a collection of Task objects, then writes a new Task to the realm:

val user = app.currentUser()
val config = SyncConfiguration.Builder(user, partition)
.allowQueriesOnUiThread(true)
.allowWritesOnUiThread(true)
.build()
Realm.getInstanceAsync(config, object : Realm.Callback() {
override fun onSuccess(realm: Realm) {
Log.v("EXAMPLE", "Successfully opened a realm.")
// Read all tasks in the realm. No special syntax required for synced realms.
val tasks = realm.where(Task::class.java).findAll()
// Write to the realm. No special syntax required for synced realms.
realm.executeTransaction { r: Realm ->
r.insert(Task())
}
// Don't forget to close your realm!
realm.close()
}
})
Tip
See also:

To pause a currently active sync session, call stop() on your SyncSession:

val session: SyncSession = app.sync.getSession(config)
session.stop()

To resume a currently paused sync session, call start() on your SyncSession:

val syncSession: SyncSession = app.sync.getSession(config)
syncSession.start()

To check the current network connection, call getConnectionState() on your SyncSession:

Log.v("EXAMPLE", "Sync state: ${app.sync.getSession(config).connectionState}")
Warning
Connection States vs. Session States

The Android SDK manages communication with MongoDB Realm at two levels: connection state and session state. Connection state tracks the state of the network connection between a client device and your backend Realm app. Session state refers to a single user's synchronization state, which can be paused and resumed in the SDK at will. As a result, you must check both states to determine whether a user's local changes will sync to the backend. Synchronization only occurs when the connection state is "connected" and the session state is "active".

You can also subscribe to connection changes on your SyncSession with addConnectionChangeListener(), which works similarly to upload and download listeners.

To subscribe to progress updates for uploads via the Android SDK, call addUploadProgressListener() on your SyncSession with a ProgressMode and a ProgressListener(). The ProgressMode passed determines which upload events your listener receives:

app.sync.getSession(config).addUploadProgressListener(
ProgressMode.INDEFINITELY) { progress ->
Log.v("EXAMPLE", "Upload progress: ${progress.fractionTransferred}")
}

To subscribe to progress updates for uploads via the Android SDK, call addDownloadProgressListener() on your SyncSession with a ProgressMode and a ProgressListener(). The ProgressMode passed determines which upload events your listener receives:

app.sync.getSession(config).addDownloadProgressListener(
ProgressMode.INDEFINITELY) { progress ->
Log.v("EXAMPLE", "Download progress: ${progress.fractionTransferred}")
}

You can configure an error handler to detect and respond to any errors that occur in the Realm Sync process. To define an error handler, pass an ErrorHandler to the SyncConfiguration.Builder.errorHandler() builder method:

val user = app.currentUser()
val config = SyncConfiguration.Builder(user, partition)
.errorHandler { session, error ->
// do some error handling
}
.build()

You can customize behavior in the event of a client reset with a custom client reset handler:

val appID: String = YOUR_APP_ID // replace this with your App ID
val handler =
ClientResetHandler { session, error ->
Log.e("EXAMPLE", "Client Reset required for: ${session.configuration.serverUrl} for error: $error")
}
val app = App(
AppConfiguration.Builder(appID)
.defaultClientResetHandler(handler)
.build()
)
Tip

To see how to recover unsynced local changes in a client reset, check out this example on GitHub.

To configure the log level for Realm logs in your application, pass a LogLevel to RealmLog.setLevel():

RealmLog.setLevel(LogLevel.ALL)
Give Feedback