← Back to Blog

Article

Offline‑First di Android: Room + WorkManager + Sync Strategy

Panduan lengkap dan mudah dipahami untuk membangun aplikasi Android offline‑first dengan Room, WorkManager, dan strategi sinkronisasi yang stabil.

2024-07-11·3 min read
AndroidKotlinOfflineRoomWorkManagerSync

Aplikasi lapangan (field app) tidak bisa bergantung pada internet. Sinyal hilang, jaringan lambat, atau device di area tertutup adalah kondisi nyata. Karena itu, offline‑first adalah pendekatan yang membuat data tetap aman, UX tetap stabil, dan user tetap percaya.

Di artikel ini kita bahas versi praktis: Room + WorkManager + strategi sync yang sederhana tapi kuat.

1) Konsep inti offline‑first (versi simpel)

Prinsipnya cuma 3:

  1. User tetap bisa bekerja saat offline
  2. Data selalu disimpan lokal dulu
  3. Sync berjalan otomatis ketika jaringan tersedia

Kalau kamu menjaga tiga hal ini, 80% masalah offline‑first sudah aman.

2) Arsitektur dasar (Outbox pattern)

Kita pakai pola Outbox:

bash
UI -> Room (local db) -> Outbox -> Worker -> API

Outbox = tabel antrean action yang perlu dikirim ke server. Semua action user masuk ke outbox dulu, bukan langsung call API.

3) Struktur data minimal

Kita butuh 2 tabel utama:

  1. Data utama (misalnya TaskEntity)
  2. Outbox untuk antrean sync

Contoh Room Entity:

kotlin
@Entity(tableName = "tasks")
data class TaskEntity(
  @PrimaryKey val id: String,
  val title: String,
  val status: String,
  val updatedAt: Long,
  val isDirty: Boolean
)
 
@Entity(tableName = "outbox")
data class OutboxEntity(
  @PrimaryKey val id: String,
  val type: String, // create/update/delete
  val payload: String,
  val createdAt: Long,
  val retryCount: Int = 0
)

isDirty dipakai untuk menandai data lokal yang belum sinkron.

4) Simpan data lokal dulu (selalu)

Saat user menambah/ubah data, kamu lakukan:

  1. Simpan ke table utama
  2. Buat item outbox
kotlin
suspend fun saveTask(task: TaskEntity) {
  taskDao.upsert(task.copy(isDirty = true, updatedAt = System.currentTimeMillis()))
  outboxDao.insert(
    OutboxEntity(
      id = UUID.randomUUID().toString(),
      type = "update",
      payload = gson.toJson(task),
      createdAt = System.currentTimeMillis()
    )
  )
}

User tetap bisa lanjut kerja tanpa menunggu network.

5) Sync Worker dengan WorkManager

Gunakan WorkManager karena:

  • otomatis retry
  • bisa set constraint (network tersedia)
  • tahan terhadap kill/background
kotlin
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
  .setConstraints(
    Constraints.Builder()
      .setRequiredNetworkType(NetworkType.CONNECTED)
      .build()
  )
  .build()
 
WorkManager.getInstance(context).enqueue(syncRequest)

6) Isi SyncWorker (intinya sederhana)

kotlin
class SyncWorker(
  context: Context,
  params: WorkerParameters
) : CoroutineWorker(context, params) {
 
  override suspend fun doWork(): Result {
    val pending = outboxDao.getAllPending()
    for (item in pending) {
      try {
        api.send(item.type, item.payload)
        outboxDao.delete(item.id)
      } catch (e: Exception) {
        outboxDao.incrementRetry(item.id)
        return Result.retry()
      }
    }
    return Result.success()
  }
}

Ini versi minimal. Di produksi kamu bisa tambah:

  • batch per 20 item
  • retry policy berbeda untuk error 4xx/5xx
  • backoff (exponential)

7) Strategi conflict handling (simple & aman)

Masalah: data di server berubah saat kamu offline.

Solusi paling aman untuk awal:

  1. Last write wins (pakai updatedAt)
  2. Jika konflik penting → tampilkan notifikasi untuk user

Contoh logic sederhana:

kotlin
if (remote.updatedAt > local.updatedAt) {
  // server lebih baru
  taskDao.upsert(remote.copy(isDirty = false))
} else {
  // lokal lebih baru
  enqueueLocalUpdate(local)
}

8) UX yang jelas (wajib!)

Offline‑first bukan cuma soal data. UX harus memberi rasa aman.

Minimal tampilkan:

  • Status koneksi (online/offline)
  • “Last synced at …”
  • Badge kecil pada item yang belum tersinkron

Contoh teks sederhana:

“Offline · data akan tersinkron otomatis”

9) Tips penting agar stabil di lapangan

  • Selalu write lokal dulu
  • Jangan block UI saat sync
  • Gunakan retry + backoff
  • Kurangi payload sync (hanya perubahan)
  • Log & analytics untuk error sync

10) Checklist produksi

Gunakan checklist ini sebelum release:

  • [ ] Semua action user masuk outbox
  • [ ] Worker punya constraint jaringan
  • [ ] Ada indikator “sync pending”
  • [ ] Konflik di-handle (minimal last-write-wins)
  • [ ] Auto retry + backoff

Penutup

Offline‑first tidak harus rumit. Pola sederhana (Room + Outbox + WorkManager) sudah cukup kuat untuk mayoritas aplikasi lapangan. Mulai dari versi minimal ini, lalu tambah fitur sesuai kebutuhan (batching, diff sync, conflict UI, dsb). Kesimpulannya: kunci keberhasilan ada di penyimpanan lokal yang konsisten, sync queue yang andal, dan UX yang memberi rasa aman.