Pola sinkronisasi latar belakang Kotlin WorkManager untuk aplikasi lapangan
Pola sinkronisasi latar belakang Kotlin WorkManager untuk aplikasi lapangan: pilih jenis kerja yang tepat, tetapkan constraint, gunakan penundaan eksponensial, dan tampilkan kemajuan yang terlihat pengguna.

Apa arti sinkronisasi latar belakang yang andal untuk aplikasi lapangan dan ops
Dalam aplikasi lapangan dan ops, sinkron bukan sekadar “bagus kalau ada”. Itu cara pekerjaan meninggalkan perangkat dan menjadi nyata bagi tim. Saat sinkron gagal, pengguna cepat menyadarinya: pekerjaan yang selesai masih terlihat “menunggu”, foto hilang, atau laporan yang sama terunggah dua kali dan membuat duplikat.
Aplikasi ini lebih sulit dibanding aplikasi konsumen biasa karena ponsel beroperasi di kondisi terburuk. Jaringan berganti antara LTE, Wi‑Fi lemah, dan tanpa sinyal. Penghemat baterai memblokir kerja latar. Aplikasi dihentikan, OS diperbarui, dan perangkat reboot di tengah rute. Setup WorkManager yang andal perlu bertahan dari semua itu tanpa drama.
Akurat secara praktis biasanya berarti empat hal:
- Eventually consistent: data mungkin tiba terlambat, tapi tiba tanpa pengawasan manual.
- Recoverable: jika aplikasi mati di tengah unggahan, run selanjutnya melanjutkan dengan aman.
- Observable: pengguna dan support bisa melihat apa yang terjadi dan apa yang macet.
- Non-destructive: retry tidak membuat duplikat atau merusak state.
“Run now” cocok untuk aksi kecil yang dipicu pengguna dan harus selesai cepat (misalnya, mengirim satu pembaruan status sebelum pengguna menutup pekerjaan). “Wait” cocok untuk pekerjaan berat seperti unggahan foto, batch update, atau apa pun yang kemungkinan menguras baterai atau gagal di jaringan buruk.
Contoh: seorang inspeksi mengirim formulir dengan 12 foto di ruang bawah tanah tanpa sinyal. Sinkron yang andal menyimpan semuanya secara lokal, menandainya sebagai antrean, dan mengunggah nanti ketika perangkat punya koneksi nyata, tanpa inspeksi mengulangi pekerjaannya.
Pilih blok bangunan WorkManager yang tepat
Mulailah dengan memilih unit kerja terkecil dan paling jelas. Keputusan itu memengaruhi keandalan lebih daripada logika retry pintar nanti.
One-time vs periodic work
Gunakan OneTimeWorkRequest untuk pekerjaan yang harus terjadi karena sesuatu berubah: formulir baru disimpan, foto selesai dikompres, atau pengguna mengetuk Sinkron. Enqueue segera (dengan constraints) dan biarkan WorkManager menjalankannya saat perangkat siap.
Gunakan PeriodicWorkRequest untuk pemeliharaan rutin, seperti “cek pembaruan” periodik atau pembersihan malam. Periodic work tidak tepat waktu. Ia punya interval minimum dan bisa bergeser berdasarkan aturan baterai dan sistem, jadi jangan mengandalkannya sebagai satu-satunya jalur untuk unggahan penting.
Pola praktis adalah one-time work untuk “harus sinkron segera”, dengan periodic work sebagai jaring pengaman.
Memilih Worker, CoroutineWorker, atau RxWorker
Jika Anda menulis Kotlin dan menggunakan fungsi suspend, utamakan CoroutineWorker. Itu membuat kode pendek dan pembatalan berperilaku seperti yang Anda harapkan.
Worker cocok untuk kode blocking sederhana, tapi Anda harus berhati-hati agar tidak memblokir terlalu lama.
RxWorker masuk akal hanya jika aplikasi Anda sudah banyak memakai RxJava. Kalau tidak, itu menambah kompleksitas.
Men-chain langkah atau menjalankan satu worker dengan fase?
Chaining bagus ketika langkah-langkah bisa berhasil atau gagal secara independen, dan Anda ingin retry terpisah serta log yang lebih jelas. Satu worker dengan fase bisa lebih baik ketika langkah berbagi data dan harus diperlakukan seperti satu transaksi.
Aturan sederhana:
- Chain ketika langkah punya constraint berbeda (unggahan hanya Wi‑Fi, lalu panggilan API ringan).
- Gunakan satu worker ketika Anda membutuhkan sinkron “all-or-nothing”.
WorkManager menjamin bahwa pekerjaan dipersistenkan, bisa bertahan dari kematian proses dan reboot, dan menghormati constraint. Ia tidak menjamin waktu eksekusi tepat, eksekusi segera, atau berjalan setelah pengguna melakukan force-stop pada aplikasi. Jika Anda membangun aplikasi lapangan Android (termasuk yang digenerasikan sebagai Kotlin dari AppMaster), rancang sinkron agar penundaan aman dan diharapkan.
Jadikan sinkron aman: idempotent, inkremental, dan dapat dilanjutkan
Aplikasi lapangan akan menjalankan ulang pekerjaan. Ponsel kehilangan sinyal, OS menghentikan proses, dan pengguna mengetuk sinkron dua kali karena tidak ada reaksi. Jika sinkron latar tidak aman untuk diulang, Anda akan mendapatkan record duplikat, pembaruan yang hilang, atau retry tak berujung.
Mulailah dengan membuat setiap panggilan server aman dijalankan dua kali. Pendekatan paling sederhana adalah idempotency key per item (misalnya UUID yang disimpan bersama record lokal) yang server perlakukan sebagai “request sama, hasil sama”. Jika Anda tidak bisa mengubah server, gunakan kunci natural yang stabil dan endpoint upsert, atau sertakan nomor versi sehingga server bisa menolak pembaruan kadaluarsa.
Lacak state lokal secara eksplisit sehingga worker bisa melanjutkan setelah crash tanpa menebak. Mesin state sederhana sering cukup:
- queued
- uploading
- uploaded
- needs-review
- failed-temporary
Jaga sinkron bersifat inkremental. Alih-alih “sinkron semua”, simpan cursor seperti lastSuccessfulTimestamp atau token server. Baca satu halaman kecil perubahan, terapkan, lalu majukan cursor hanya setelah batch sepenuhnya dikomit secara lokal. Batch kecil (mis. 20–100 item) mengurangi timeout, membuat kemajuan terlihat, dan membatasi berapa banyak kerja yang Anda ulang setelah interupsi.
Buat unggahan bisa dilanjutkan juga. Untuk foto atau payload besar, persist URI file dan metadata unggahan, dan tandai sebagai terunggah hanya setelah server mengonfirmasi. Jika worker dimulai ulang, ia melanjutkan dari state terakhir yang diketahui daripada memulai dari awal.
Contoh: teknisi mengisi 12 formulir dan melampirkan 8 foto di bawah tanah. Ketika perangkat terkoneksi kembali, worker mengunggah dalam batch, setiap formulir punya idempotency key, dan cursor sinkron maju hanya setelah setiap batch sukses. Jika aplikasi dihentikan di tengah, menjalankan ulang worker menyelesaikan item antrean yang tersisa tanpa menggandakan apa pun.
Constraint yang cocok dengan kondisi perangkat nyata
Constraint adalah pembatas yang menjaga sinkron latar dari menguras baterai, membakar kuota data, atau gagal pada waktu terburuk. Anda menginginkan constraint yang mencerminkan cara perangkat berperilaku di lapangan, bukan di meja pengembang Anda.
Mulailah dengan set kecil yang melindungi pengguna namun tetap memungkinkan pekerjaan berjalan sebagian besar hari. Baseline praktis: memerlukan koneksi jaringan, hindari berjalan saat baterai rendah, dan hindari saat penyimpanan kritis rendah. Tambahkan “charging” hanya jika pekerjaan berat dan tidak sensitif waktu, karena banyak perangkat lapangan jarang tercolok selama jam kerja.
Terlalu banyak constraint adalah alasan umum laporan “sinkron tidak pernah berjalan”. Jika Anda meminta Wi‑Fi tidak berbayar, sedang mengisi daya, dan baterai tidak rendah, Anda pada dasarnya meminta momen sempurna yang mungkin tak pernah datang. Jika bisnis perlu data hari ini, lebih baik menjalankan pekerjaan kecil lebih sering daripada menunggu kondisi ideal.
Captive portal adalah masalah dunia nyata lain: ponsel mengatakan terhubung, tapi pengguna harus mengetuk “Accept” di halaman Wi‑Fi hotel atau publik. WorkManager tidak bisa mendeteksi status itu secara andal. Perlakukan sebagai kegagalan normal: coba sinkron, timeout cepat, dan retry nanti. Juga tunjukkan pesan sederhana dalam aplikasi seperti “Terhubung ke Wi‑Fi tetapi tanpa akses internet” jika Anda bisa mendeteksinya selama request.
Gunakan constraint berbeda untuk unggahan kecil vs besar supaya aplikasi tetap responsif:
- Payload kecil (status ping, metadata formulir): jaringan apa saja, baterai tidak rendah.
- Payload besar (foto, video, paket peta): jaringan unmetered bila memungkinkan, dan pertimbangkan charging.
Contoh: teknisi menyimpan formulir dengan 2 foto. Kirim field formulir pada koneksi apa pun, tapi antrekan unggahan foto untuk menunggu Wi‑Fi atau momen yang lebih baik. Kantor melihat pekerjaan cepat, dan perangkat tidak menghabiskan kuota mobile mengunggah gambar di latar.
Retry dengan exponential backoff yang tidak mengganggu pengguna
Retry adalah tempat aplikasi lapangan terasa tenang atau terasa rusak. Pilih kebijakan backoff yang cocok dengan jenis kegagalan yang Anda harapkan.
Exponential backoff biasanya default paling aman untuk jaringan. Ia cepat meningkatkan waktu tunggu sehingga Anda tidak membombardir server atau menguras baterai saat coverage buruk. Linear backoff bisa cocok untuk masalah sementara singkat (mis. VPN yang fluktuatif), tapi cenderung retry terlalu sering di area sinyal lemah.
Buat keputusan retry berdasarkan tipe kegagalan, bukan hanya “ada yang gagal”. Aturan sederhana membantu:
- Network timeout, 5xx, DNS, tidak ada konektivitas:
Result.retry() - Auth expired (401): refresh token sekali, lalu gagal dan minta pengguna masuk lagi
- Validation atau 4xx (bad request):
Result.failure()dengan error jelas untuk support - Conflict (409) untuk item yang sudah dikirim: perlakukan sebagai sukses jika sinkron Anda idempotent
Batasi kerusakan sehingga error permanen tidak looping selamanya. Tetapkan jumlah percobaan maksimum, dan setelah itu, hentikan dan munculkan satu pesan yang tenang dan dapat ditindaklanjuti (bukan notifikasi berulang).
Anda juga bisa mengubah perilaku saat percobaan bertambah. Contoh: setelah 2 kegagalan, kirim batch lebih kecil atau lewati unggahan besar sampai pull berikutnya sukses.
val request = OneTimeWorkRequestBuilder<SyncWorker>()
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
30, TimeUnit.SECONDS
)
.build()
// in doWork()
if (runAttemptCount >= 5) return Result.failure()
return Result.retry()
Ini membuat retry lebih sopan: lebih sedikit wakeup, lebih sedikit gangguan pengguna, dan pemulihan lebih cepat ketika koneksi akhirnya kembali.
Progres yang terlihat pengguna: notifikasi, foreground work, dan status
Aplikasi lapangan sering sinkron saat pengguna paling tidak mengharapkannya: di bawah tanah, di jaringan lambat, dengan baterai hampir habis. Jika sinkron memengaruhi apa yang ditunggu pengguna (unggahan, pengiriman laporan, batch foto), buat itu terlihat dan mudah dimengerti. Kerja latar senyap bagus untuk pembaruan kecil dan cepat. Apa pun yang lebih lama harus terasa jujur.
Kapan diperlukan foreground work
Gunakan eksekusi foreground ketika job itu long-running, sensitif waktu, atau jelas terkait tindakan pengguna. Di Android modern, unggahan besar bisa dihentikan atau ditunda kecuali Anda menjalankannya sebagai foreground. Dalam WorkManager, itu berarti mengembalikan ForegroundInfo sehingga sistem menampilkan notifikasi berjalan.
Notifikasi yang baik menjawab tiga pertanyaan: apa yang sedang disinkronkan, seberapa jauh, dan bagaimana cara menghentikannya. Tambahkan aksi batal yang jelas agar pengguna bisa mundur jika mereka sedang di data berbayar atau butuh ponsel sekarang.
Progres yang dapat dipercaya
Progres harus memetakan ke unit nyata, bukan persentase kabur. Perbarui progres dengan setProgress dan bacalah dari WorkInfo di UI Anda (atau layar status).
Jika Anda mengunggah 12 foto dan 3 formulir, laporkan “5 dari 15 item terunggah”, tunjukkan yang tersisa, dan simpan pesan error terakhir untuk support.
Jaga progres bermakna:
- Item selesai dan item tersisa
- Langkah saat ini ("Uploading photos", "Sending forms", "Finalizing")
- Waktu sinkron terakhir yang sukses
- Error terakhir (singkat, ramah pengguna)
- Opsi batal/stop yang terlihat
Jika tim Anda membangun alat internal cepat dengan AppMaster, pakai aturan yang sama: pengguna percaya sinkron ketika mereka bisa melihatnya, dan ketika itu sesuai dengan apa yang mereka coba capai.
Unique work, tag, dan menghindari job sinkron ganda
Job sinkron ganda adalah salah satu cara paling mudah menguras baterai, membakar data mobile, dan menciptakan konflik sisi server. WorkManager memberi dua alat sederhana untuk mencegah itu: nama kerja unik dan tag.
Default yang baik adalah memperlakukan “sync” sebagai satu jalur. Alih-alih enqueue job baru setiap kali aplikasi bangun, enqueue dengan nama unique work yang sama. Dengan begitu, Anda tidak mendapatkan badai sinkron saat pengguna membuka aplikasi, perubahan jaringan terjadi, dan job periodik juga ikut memicu.
val request = OneTimeWorkRequestBuilder<SyncWorker>()
.addTag("sync")
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork("sync", ExistingWorkPolicy.KEEP, request)
Memilih policy adalah pilihan perilaku utama:
KEEP: jika sinkron sudah berjalan (atau queued), abaikan request baru. Gunakan ini untuk sebagian besar tombol “Sync now” dan trigger auto-sync.REPLACE: batalkan yang sedang berjalan dan mulai ulang. Gunakan ini ketika input benar-benar berubah, seperti pengguna mengganti akun atau memilih proyek berbeda.
Tag adalah pegangan Anda untuk kontrol dan visibilitas. Dengan tag stabil seperti sync, Anda bisa membatalkan, memeriksa status, atau memfilter log tanpa melacak ID spesifik. Ini sangat berguna untuk aksi manual “sync now”: Anda bisa memeriksa apakah sudah ada kerja yang berjalan dan menampilkan pesan jelas alih-alih meluncurkan worker lain.
Periodic dan on-demand sync tidak boleh saling berkonflik. Jaga agar mereka terpisah, tapi terkoordinasi:
- Gunakan
enqueueUniquePeriodicWork("sync_periodic", KEEP, ...)untuk job terjadwal. - Gunakan
enqueueUniqueWork("sync", KEEP, ...)untuk on-demand. - Dalam worker Anda, keluar cepat jika tidak ada yang harus diunggah atau diunduh, sehingga run periodik tetap murah.
- Opsional: biarkan worker periodik enqueue unique one-time sync yang sama, sehingga semua kerja nyata terjadi di satu tempat.
Pola ini membuat sinkron latar dapat diprediksi: satu sinkron pada satu waktu, mudah dibatalkan, dan mudah diamati.
Langkah demi langkah: pipeline sinkron latar praktis
Pipeline sinkron yang andal lebih mudah dibangun jika Anda memperlakukannya seperti mesin state kecil: item kerja hidup lokal dulu, dan WorkManager hanya memindahkannya maju ketika kondisi tepat.
Pipeline sederhana yang bisa Anda kirim
-
Mulai dengan tabel antrean lokal. Simpan metadata paling kecil yang Anda butuhkan untuk melanjutkan: id item, tipe (form, photo, note), status (pending, uploading, done), jumlah percobaan, error terakhir, dan cursor atau revisi server untuk download.
-
Untuk “Sync now” yang dipicu pengguna, enqueue
OneTimeWorkRequestdengan constraint yang cocok dengan dunia nyata Anda. Pilihan umum adalah jaringan terhubung dan baterai tidak rendah. Jika unggahan berat, juga set charging. -
Implementasikan satu
CoroutineWorkerdengan fase jelas: upload, download, reconcile. Jaga setiap fase inkremental. Upload hanya item yang bertanda pending, download hanya perubahan sejak cursor terakhir Anda, lalu reconcile konflik dengan aturan sederhana (mis. server menang untuk field penugasan, client menang untuk catatan draft lokal). -
Tambahkan retry dengan backoff, tapi selektif tentang apa yang Anda retry. Timeout dan 500 harus retry. 401 (logged out) harus gagal cepat dan memberi tahu UI apa yang terjadi.
-
Observasi
WorkInfountuk menggerakkan UI dan notifikasi. Gunakan update progress untuk fase seperti “Uploading 3 of 10”, dan tampilkan pesan kegagalan singkat yang menunjuk pada tindakan selanjutnya (retry, sign in, connect to Wi‑Fi).
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val request = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
Ketika Anda menjaga antrean lokal dan fase worker eksplisit, Anda mendapatkan perilaku yang dapat diprediksi: pekerjaan bisa pause, resume, dan menjelaskan dirinya sendiri ke pengguna tanpa menebak apa yang terjadi.
Kesalahan umum dan jebakan (dan cara menghindarinya)
Sinkron yang andal paling sering gagal karena beberapa pilihan kecil yang terlihat aman saat pengujian, lalu rontok di perangkat nyata. Tujuannya bukan menjalankan sesering mungkin. Tujuannya menjalankan pada waktu yang tepat, melakukan kerja yang tepat, dan berhenti dengan rapi saat tidak mungkin.
Jebakan yang perlu diwaspadai
- Melakukan unggahan besar tanpa constraint. Jika Anda mendorong foto atau payload besar di jaringan apa pun dan level baterai apa pun, pengguna akan merasakannya. Tambahkan constraint untuk tipe jaringan dan baterai rendah, dan bagi pekerjaan besar menjadi potongan lebih kecil.
- Retry setiap kegagalan selamanya. 401, token kadaluarsa, atau izin hilang bukan masalah sementara. Tandai sebagai kegagalan keras, munculkan tindakan jelas (login ulang), dan hanya retry isu transien seperti timeouts.
- Membuat duplikat secara tidak sengaja. Jika worker bisa berjalan dua kali, server akan melihat create ganda kecuali request idempotent. Gunakan ID yang dihasilkan client per item dan buat server memperlakukan ulang sebagai update, bukan record baru.
- Menggunakan periodic work untuk kebutuhan near real-time. Periodic work terbaik untuk pemeliharaan, bukan “sync now”. Untuk sinkron yang dipicu pengguna, enqueue one-time unique work dan biarkan pengguna memicunya saat dibutuhkan.
- Melaporkan “100%” terlalu dini. Penyelesaian unggahan tidak sama dengan data diterima dan direkonsiliasi. Lacak progres per tahap (queued, uploading, server confirmed) dan hanya tunjukkan selesai setelah konfirmasi.
Contoh konkret: teknisi mengirim formulir dengan tiga foto di lift dengan sinyal lemah. Jika Anda mulai langsung tanpa constraint, unggahan macet, retry melonjak, dan formulir bisa dibuat dua kali saat aplikasi restart. Jika Anda membatasi ke jaringan yang bisa dipakai, unggah bertahap, dan beri setiap formulir ID stabil, skenario yang sama berakhir dengan satu record server bersih dan pesan progres yang jujur.
Daftar periksa cepat sebelum rilis
Sebelum rilis, uji sinkron seperti cara pengguna lapangan nyata akan memecahkannya: sinyal putus-putus, baterai mati, dan banyak ketukan. Apa yang terlihat baik di ponsel dev bisa saja gagal di lapangan jika penjadwalan, retry, atau pelaporan status tidak tepat.
Jalankan pemeriksaan ini setidaknya pada satu perangkat lambat dan satu perangkat lebih baru. Simpan log, tapi juga perhatikan apa yang dilihat pengguna di UI.
- Tidak ada jaringan, lalu pulih: Mulai sinkron dengan konektivitas mati, lalu hidupkan kembali. Konfirmasi pekerjaan antre (bukan gagal cepat), dan melanjutkan nanti tanpa menggandakan unggahan.
- Restart perangkat: Mulai sinkron, reboot di tengah, lalu buka kembali aplikasi. Verifikasi pekerjaan berlanjut atau dijadwalkan ulang dengan benar, dan aplikasi menunjukkan state saat ini yang benar (tidak terjebak di "syncing").
- Baterai rendah dan penyimpanan rendah: Aktifkan penghemat baterai, turun di bawah ambang baterai rendah jika memungkinkan, dan isi penyimpanan hampir penuh. Konfirmasi job menunggu saat seharusnya, lalu melanjutkan setelah kondisi membaik, tanpa membakar baterai dalam loop retry.
- Trigger berulang: Ketuk tombol "Sync" beberapa kali, atau picu sinkron dari banyak layar. Anda seharusnya tetap mendapatkan satu run sinkron logis, bukan tumpukan worker paralel yang saling berebut record yang sama.
- Kegagalan server yang bisa Anda jelaskan: Simulasikan 500s, timeout, dan error auth. Periksa bahwa retry men-backoff dan berhenti setelah batas, dan pengguna melihat pesan jelas seperti "Tidak bisa mencapai server, akan mencoba lagi" alih-alih gagal generik.
Jika ada tes yang meninggalkan aplikasi dalam state tidak jelas, anggap itu bug. Pengguna memaafkan sinkron lambat, tetapi mereka tidak memaafkan kehilangan data atau tidak tahu apa yang terjadi.
Contoh skenario: formulir offline dan unggahan foto di aplikasi lapangan
Seorang teknisi tiba di lokasi dengan coverage lemah. Mereka mengisi formulir layanan offline, mengambil 12 foto, dan mengetuk Submit sebelum pergi. Aplikasi menyimpan semuanya secara lokal terlebih dulu (mis. di database lokal): satu record untuk formulir, dan satu record per foto dengan state jelas seperti PENDING, UPLOADING, DONE, atau FAILED.
Saat mereka mengetuk Submit, aplikasi enqueue unique sync job sehingga tidak membuat duplikat jika mereka mengetuk dua kali. Setup umum adalah chain WorkManager yang mengunggah foto dulu (lebih besar, lebih lambat), lalu mengirim payload formulir setelah lampiran terkonfirmasi.
Sinkron hanya berjalan ketika kondisi cocok dengan dunia nyata. Misalnya, menunggu koneksi terhubung, baterai tidak rendah, dan penyimpanan cukup. Jika teknisi masih di ruang bawah tanah tanpa sinyal, tidak ada yang membakar baterai dalam loop di latar.
Progres jelas dan ramah pengguna. Unggahan berjalan sebagai foreground work dan menunjukkan notifikasi seperti “Uploading 3 of 12”, dengan aksi Cancel yang jelas. Jika mereka membatalkan, aplikasi menghentikan kerja dan menyimpan item tersisa di PENDING sehingga bisa dicoba lagi nanti tanpa kehilangan data.
Retry berperilaku sopan setelah hotspot yang fluktuatif: kegagalan pertama retry segera, tetapi setiap kegagalan menunggu lebih lama (penundaan eksponensial). Rasanya responsif di awal, lalu mundur agar tidak menguras baterai dan mem-banjiri jaringan.
Bagi tim ops, keuntungannya nyata: lebih sedikit pengiriman duplikat karena item idempotent dan antrean unik, status kegagalan yang jelas (foto mana yang gagal, kenapa, dan kapan akan mencoba lagi), dan kepercayaan lebih besar bahwa “submitted” berarti “disimpan dengan aman dan akan sinkron”.
Langkah berikutnya: kirim reliabilitas dulu, lalu perluas cakupan sinkron
Sebelum menambah lebih banyak fitur sinkron, pastikan jelas apa arti “selesai”. Untuk sebagian besar aplikasi lapangan, bukan hanya “request terkirim”. Itu berarti “server menerima dan mengonfirmasi”, plus state UI yang sesuai dengan kenyataan. Form yang menampilkan “Synced” harus tetap begitu setelah restart aplikasi, dan form yang gagal harus menunjukkan apa yang harus dilakukan selanjutnya.
Buat aplikasi mudah dipercaya dengan menambahkan set kecil sinyal yang bisa dilihat orang (dan ditanyakan support). Jaga mereka sederhana dan konsisten di seluruh layar:
- Waktu sinkron terakhir yang sukses
- Error sinkron terakhir (pesan singkat, bukan stack trace)
- Item yang menunggu (mis. 3 formulir, 12 foto)
- State sinkron saat ini (Idle, Syncing, Needs attention)
Perlakukan observability sebagai bagian dari fitur. Itu menghemat jam di lapangan ketika seseorang berada di koneksi lemah dan tidak tahu apakah aplikasi bekerja.
Jika Anda juga membangun backend dan alat admin, menghasilkannya bersama membantu menjaga kontrak sinkron tetap stabil. AppMaster (appmaster.io) bisa menghasilkan backend siap produksi, panel admin web, dan aplikasi mobile native, yang membantu menyelaraskan model dan auth sementara Anda fokus pada ujung-ujung sinkron yang rumit.
Akhirnya, jalankan pilot kecil. Pilih satu slice end-to-end sinkron (mis. “kirim formulir inspeksi dengan 1–2 foto”), dan kirimkan dengan constraint, retry, dan progres yang terlihat pengguna berfungsi penuh. Ketika slice itu membosankan dan dapat diprediksi, perluas fitur satu per satu.
FAQ
Sinkronisasi latar belakang yang andal berarti pekerjaan yang dibuat di perangkat disimpan secara lokal terlebih dulu dan akan diunggah nanti tanpa pengguna mengulangi langkah. Ia harus bertahan terhadap terhentinya aplikasi, reboot, jaringan lemah, dan retry tanpa kehilangan data atau membuat duplikat.
Gunakan one-time work untuk apa pun yang dipicu oleh sebuah peristiwa nyata seperti “form disimpan”, “foto ditambahkan”, atau pengguna menekan Sinkron. Gunakan periodic work untuk pemeliharaan dan sebagai jaring pengaman, tapi jangan mengandalkannya sebagai satu-satunya jalur untuk unggahan penting karena waktunya bisa bergeser.
Jika Anda memakai Kotlin dan kode sinkron Anda menggunakan fungsi suspend, CoroutineWorker adalah pilihan yang paling sederhana dan paling dapat diprediksi, terutama untuk pembatalan. Gunakan Worker hanya untuk tugas blocking singkat, dan RxWorker hanya jika aplikasi Anda memang berat menggunakan RxJava.
Chain worker ketika langkah-langkah memiliki constraint berbeda atau harus retry secara terpisah, misalnya mengunggah file besar hanya di Wi‑Fi lalu melakukan panggilan API kecil di jaringan apa saja. Gunakan satu worker dengan fase jelas ketika langkah-langkah berbagi state dan Anda menginginkan perilaku “all-or-nothing” untuk satu run sinkron logis.
Jadikan setiap request create/update aman dijalankan dua kali dengan memakai idempotency key per item (seringkali UUID yang disimpan bersama record lokal). Jika Anda tidak bisa mengubah server, gunakan upsert dengan kunci stabil atau cek versi sehingga pengulangan tidak membuat baris baru.
Simpan status lokal eksplisit seperti queued, uploading, uploaded, dan failed sehingga worker bisa melanjutkan tanpa menebak. Tandai item sebagai selesai hanya setelah server mengonfirmasi, dan simpan metadata yang cukup (mis. URI file dan jumlah percobaan) agar bisa melanjutkan setelah crash atau reboot.
Mulailah dengan constraint minimal yang melindungi pengguna tapi tetap memungkinkan sinkron berjalan hampir setiap hari: memerlukan koneksi jaringan, hindari berjalan saat baterai rendah, dan hindari saat penyimpanan kritis. Hati-hati dengan syarat “unmetered” dan “charging” karena itu dapat membuat sinkron jarang terjadi pada perangkat lapangan nyata.
Anggap “terhubung tapi tanpa internet” sebagai kegagalan biasa: time out cepat, kembalikan Result.retry(), dan coba lagi nanti. Jika Anda bisa mendeteksinya saat request, tunjukkan pesan sederhana agar pengguna mengerti kenapa perangkat terlihat online tetapi sinkron tidak maju.
Gunakan exponential backoff untuk kegagalan jaringan sehingga retry menjadi lebih jarang saat coverage buruk. Retry timeouts dan error 5xx, gagal cepat pada masalah permanen seperti request yang tidak valid, dan batasi jumlah percobaan sehingga Anda tidak looping selamanya ketika pengguna harus mengambil tindakan (mis. login ulang).
Enqueue sinkron sebagai unique work sehingga trigger berulang tidak memulai job paralel, dan tampilkan progres yang bisa dipercaya pengguna untuk unggahan panjang. Jika pekerjaan itu berjalan lama atau dipicu pengguna, jalankan sebagai foreground work dengan notifikasi berjalan yang menunjukkan hitungan nyata dan menawarkan opsi batal yang jelas.


