| name | mobile-sync-android | |||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| summary | Couchbase Mobile sync for Android (Kotlin) — configure Couchbase Lite replication, set up Sync Gateway, design Sync Function access control, implement offline-first architecture | |||||||||||||||||||||||||||||
| description | Couchbase Mobile sync for Android (Kotlin) — configure Couchbase Lite replication, set up Sync Gateway, design Sync Function access control, implement offline-first architecture | |||||||||||||||||||||||||||||
| compatibility | Couchbase Lite Android 4.x (Kotlin). Requires Sync Gateway 3.x or Capella App Services for cloud sync. | |||||||||||||||||||||||||||||
| metadata |
|
Platform-agnostic concepts (architecture, Sync Function, troubleshooting): shared/mobile/sync-architecture.md
Configure Couchbase Lite replication to Sync Gateway or Capella App Services.
Sync Function template: templates/sync-function.js — copy this skeleton as a starting point.
Android Device
└── Couchbase Lite (embedded JSON database)
│ WebSocket (wss://)
▼
Sync Gateway / Capella App Services
│
▼
Couchbase Server / Capella
import com.couchbase.lite.*
val target = URLEndpoint(URI("wss://sync-gateway.example.com/mydb"))
val replConfig = ReplicatorConfiguration(target).apply {
addCollection(collection, null)
type = ReplicatorType.PUSH_AND_PULL
continuous = true
// Read from EncryptedSharedPreferences (androidx.security.crypto) — never hardcode
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
val encryptedPrefs = EncryptedSharedPreferences.create(
context, "sg_creds", masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
authenticator = BasicAuthenticator(
encryptedPrefs.getString("sg_username", null)!!,
encryptedPrefs.getString("sg_password", null)!!.toCharArray()
)
}
val replicator = Replicator(replConfig)
val token = replicator.addChangeListener { change ->
val status = change.status
when (status.activityLevel) {
ReplicatorActivityLevel.STOPPED -> Log.i("Sync", "Stopped: ${status.error}")
ReplicatorActivityLevel.OFFLINE -> Log.i("Sync", "Offline")
ReplicatorActivityLevel.IDLE -> Log.i("Sync", "Idle — up to date")
ReplicatorActivityLevel.BUSY -> Log.i("Sync", "Syncing...")
else -> {}
}
}
replicator.start()val replConfig = ReplicatorConfiguration(target).apply {
addCollection(collection, null)
authenticator = SessionAuthenticator("your-session-token")
}val collectionConfig = CollectionConfiguration().apply {
channels = listOf("user.alice", "team.engineering")
}
val replConfig = ReplicatorConfiguration(target).apply {
addCollection(collection, collectionConfig)
}val collectionConfig = CollectionConfiguration().apply {
pushFilter = ReplicationFilter { document, _ ->
document.getString("type") != "draft"
}
}// Always read/write from the local database — replication happens in the background
fun saveNote(note: Note) {
val doc = MutableDocument("note::${note.id}").apply {
setString("title", note.title)
setString("body", note.body)
setString("userId", currentUser.id)
}
collection.save(doc) // local write, syncs automatically
}- 401: wrong credentials or session expired
- 403: Sync Function
requireUser/requireRolerejected the document - WebSocket CLOSE 1008: Sync Gateway rejected the connection — check Sync Gateway logs
- Enable
REPLICATORandNETWORKatDEBUG(seemobile-logging-android)