Aplikasi offline-first adalah aplikasi yang dapat melakukan semuanya atau subset penting dalam fungsi intinya, tanpa akses ke internet. Artinya, aplikasi dapat menjalankan sebagian atau semua logika bisnisnya secara offline.
Sebaiknya Anda memulai pembuatan aplikasi offline-first dari lapisan data yang menawarkan akses ke data aplikasi dan logika bisnis. Aplikasi mungkin perlu memperbarui data ini dari waktu ke waktu dari sumber di luar perangkat. Untuk melakukannya, Anda mungkin perlu memanggil resource jaringan agar tetap mendapatkan update terbaru.
Ketersediaan jaringan tidak selalu dijamin. Perangkat biasanya memiliki periode koneksi jaringan yang tidak stabil atau lambat. Pengguna mungkin mengalami hal berikut:
- Bandwidth internet terbatas.
- Gangguan koneksi sementara, seperti saat berada di elevator atau terowongan.
- Akses data terputus-putus. Misalnya, tablet khusus Wi-Fi.
Apa pun alasannya, aplikasi sering kali dapat berfungsi secara memadai dalam situasi ini. Untuk memastikan aplikasi Anda berfungsi dengan benar secara offline, aplikasi harus dapat melakukan hal berikut:
- Dapat terus digunakan tanpa koneksi jaringan yang andal.
- Menampilkan data lokal kepada pengguna secara langsung, bukan menunggu panggilan jaringan pertama selesai atau gagal.
- Mengambil data dengan cara yang mengenali status data dan baterai. Misalnya, dengan hanya meminta pengambilan data dalam kondisi yang optimal, seperti saat mengisi daya atau terhubung ke Wi-Fi.
Aplikasi yang dapat memenuhi kriteria di atas sering disebut aplikasi offline-first.
Mendesain aplikasi offline-first
Saat mendesain aplikasi offline-first, Anda harus memulai dari lapisan data dan dua operasi utama yang dapat Anda lakukan pada data aplikasi:
- Operasi baca: Mengambil data untuk digunakan oleh bagian lain aplikasi seperti menampilkan informasi kepada pengguna.
- Operasi tulis: Menyimpan input pengguna untuk pengambilan nanti.
Repositori di lapisan data bertanggung jawab untuk menggabungkan sumber data guna menyediakan data aplikasi. Dalam aplikasi offline-first, setidaknya harus ada satu sumber data yang tidak memerlukan akses jaringan untuk melakukan tugas yang paling penting. Salah satu tugas penting ini adalah membaca data.
Data model di aplikasi offline-first
Aplikasi offline-first memiliki minimal 2 sumber data untuk setiap repositori yang menggunakan resource jaringan:
- Sumber data lokal
- Sumber data jaringan
![Lapisan data offline-first terdiri dari sumber data lokal dan jaringan](https://developer.android.com/static/images/topic/architecture/data-layer/data-layer.png?authuser=8&hl=id)
Sumber data lokal
Sumber data lokal adalah sumber tepercaya kanonis untuk aplikasi. Sumber ini harus menjadi sumber eksklusif setiap data yang dibaca oleh lapisan aplikasi yang lebih tinggi. Hal ini memastikan konsistensi data di antara status koneksi. Sumber data lokal sering kali didukung oleh penyimpanan yang disimpan ke disk. Beberapa cara umum untuk menyimpan data ke disk adalah sebagai berikut:
- Sumber data terstruktur, seperti database relasional, seperti Room.
- Sumber data tidak terstruktur. Misalnya, buffering protokol dengan Datastore.
- File sederhana
Sumber data jaringan
Sumber data jaringan adalah status aplikasi yang sebenarnya. Sumber data lokal
paling baik disinkronkan dengan sumber data jaringan. Sumber ini juga dapat mengalami
lag, sehingga aplikasi perlu diupdate saat kembali online.
Sebaliknya, sumber data jaringan mungkin mengalami lag di belakang sumber data lokal hingga
aplikasi dapat mengupdatenya saat kembali terhubung ke internet. Lapisan domain dan UI
aplikasi tidak boleh bekerja sama dengan lapisan jaringan secara langsung. Host repository
bertanggung jawab untuk berkomunikasi dengannya, dan menggunakannya untuk
mengupdate sumber data lokal.
Mengekspos resource
Sumber data lokal dan jaringan dapat memiliki perbedaan secara mendasar terkait cara aplikasi membaca dan menulis ke sumber data tersebut. Membuat kueri sumber data lokal dapat dilakukan dengan cepat dan fleksibel, misalnya saat menggunakan kueri SQL. Sebaliknya, sumber data jaringan dapat menjadi lambat dan dibatasi, seperti saat mengakses resource RESTful secara bertahap menurut ID. Akibatnya, setiap sumber data sering kali memerlukan representasi tersendiri untuk data yang diberikan. Oleh karena itu, sumber data lokal dan sumber data jaringan mungkin memiliki modelnya sendiri.
Struktur direktori di bawah memvisualisasikan konsep ini. AuthorEntity
adalah
representasi penulis yang dibaca dari database lokal aplikasi, dan
NetworkAuthor
adalah representasi penulis yang diserialisasi melalui jaringan:
data/
├─ local/
│ ├─ entities/
│ │ ├─ AuthorEntity
│ ├─ dao/
│ ├─ NiADatabase
├─ network/
│ ├─ NiANetwork
│ ├─ models/
│ │ ├─ NetworkAuthor
├─ model/
│ ├─ Author
├─ repository/
Detail AuthorEntity
dan NetworkAuthor
berikut:
/**
* Network representation of [Author]
*/
@Serializable
data class NetworkAuthor(
val id: String,
val name: String,
val imageUrl: String,
val twitter: String,
val mediumPage: String,
val bio: String,
)
/**
* Defines an author for either an [EpisodeEntity] or [NewsResourceEntity].
* It has a many-to-many relationship with both entities
*/
@Entity(tableName = "authors")
data class AuthorEntity(
@PrimaryKey
val id: String,
val name: String,
@ColumnInfo(name = "image_url")
val imageUrl: String,
@ColumnInfo(defaultValue = "")
val twitter: String,
@ColumnInfo(name = "medium_page", defaultValue = "")
val mediumPage: String,
@ColumnInfo(defaultValue = "")
val bio: String,
)
Sebaiknya simpan AuthorEntity
dan NetworkAuthor
secara internal ke lapisan data dan ekspos jenis ketiga yang dapat digunakan oleh
lapisan eksternal. Tindakan ini melindungi lapisan eksternal dari perubahan kecil pada sumber data lokal
dan jaringan yang tidak mengubah perilaku aplikasi secara mendasar.
Hal ini ditunjukkan dalam cuplikan berikut:
/**
* External data layer representation of a "Now in Android" Author
*/
data class Author(
val id: String,
val name: String,
val imageUrl: String,
val twitter: String,
val mediumPage: String,
val bio: String,
)
Model jaringan kemudian dapat menentukan metode ekstensi untuk mengonversinya menjadi model lokal, dan model lokal juga dapat menentukan metode untuk mengonversinya menjadi representasi eksternal seperti yang ditunjukkan di bawah ini:
/**
* Converts the network model to the local model for persisting
* by the local data source
*/
fun NetworkAuthor.asEntity() = AuthorEntity(
id = id,
name = name,
imageUrl = imageUrl,
twitter = twitter,
mediumPage = mediumPage,
bio = bio,
)
/**
* Converts the local model to the external model for use
* by layers external to the data layer
*/
fun AuthorEntity.asExternalModel() = Author(
id = id,
name = name,
imageUrl = imageUrl,
twitter = twitter,
mediumPage = mediumPage,
bio = bio,
)
Operasi baca
Operasi baca adalah operasi mendasar pada data aplikasi di aplikasi offline-first. Oleh karena itu, Anda harus memastikan bahwa aplikasi dapat membaca data, dan segera menampilkan data baru begitu tersedia. Aplikasi yang dapat melakukannya adalah aplikasi reaktif karena mengekspos API baca dengan jenis observable.
Dalam cuplikan di bawah ini, OfflineFirstTopicRepository
menampilkan Flows
untuk semua
API bacanya. Hal ini memungkinkan repositori mengupdate pembacanya saat menerima update
dari sumber data jaringan. Dengan kata lain, repositori memungkinkan
push OfflineFirstTopicRepository
berubah saat sumber data lokalnya
dibatalkan. Oleh karena itu, setiap pembaca OfflineFirstTopicRepository
harus
disiapkan untuk menangani perubahan data yang dapat dipicu saat konektivitas
jaringan dipulihkan ke aplikasi. Selain itu, OfflineFirstTopicRepository
akan membaca data
secara langsung dari sumber data lokal. Elemen ini hanya dapat memberi tahu pembacanya
tentang perubahan data dengan mengupdate sumber data lokalnya terlebih dahulu.
class OfflineFirstTopicsRepository(
private val topicDao: TopicDao,
private val network: NiaNetworkDataSource,
) : TopicsRepository {
override fun getTopicsStream(): Flow<List<Topic>> =
topicDao.getTopicEntitiesStream()
.map { it.map(TopicEntity::asExternalModel) }
}
Strategi penanganan error
Ada cara unik untuk menangani error di aplikasi offline-first, bergantung pada sumber data tempat error tersebut terjadi. Sub-bagian berikut menguraikan strategi ini.
Sumber data lokal
Error saat membaca dari sumber data lokal akan jarang terjadi. Untuk melindungi
pembaca dari error, gunakan operator catch
pada Flows
tempat
pembaca mengumpulkan data.
Penggunaan operator catch
di ViewModel
adalah sebagai berikut:
class AuthorViewModel(
authorsRepository: AuthorsRepository,
...
) : ViewModel() {
private val authorId: String = ...
// Observe author information
private val authorStream: Flow<Author> =
authorsRepository.getAuthorStream(
id = authorId
)
.catch { emit(Author.empty()) }
}
Sumber data jaringan
Jika terjadi error saat membaca data dari sumber data jaringan, aplikasi harus menggunakan heuristik untuk mencoba mengambil data lagi. Heuristik umum meliputi:
Backoff eksponensial
Dalam backoff eksponensial, aplikasi terus mencoba membaca dari sumber data jaringan dengan interval waktu yang meningkat hingga berhasil, atau kondisi lain yang menentukan bahwa aplikasi harus berhenti.
![Membaca data dengan backoff eksponensial](https://developer.android.com/static/images/topic/architecture/data-layer/read-backoff.png?authuser=8&hl=id)
Kriteria untuk mengevaluasi apakah aplikasi harus tetap melakukan backoff meliputi:
- Jenis error yang ditunjukkan oleh sumber data jaringan. Misalnya, Anda harus mencoba lagi panggilan jaringan yang menampilkan error kurangnya konektivitas. Sebaliknya, Anda tidak boleh mencoba lagi permintaan HTTP yang tidak diotorisasi hingga kredensial yang tepat tersedia.
- Percobaan ulang maksimum yang diperbolehkan.
Pemantauan konektivitas jaringan
Dalam pendekatan ini, permintaan baca dimasukkan ke dalam antrean sampai aplikasi yakin dapat terhubung ke sumber data jaringan. Setelah koneksi dibuat, permintaan baca kemudian dikeluarkan dari antrean, data dibaca, dan sumber data lokal diupdate. Di Android, antrean ini dapat dikelola dengan database Room, dan dikosongkan sebagai tugas persisten menggunakan WorkManager.
![Membaca data dengan antrean dan pemantauan jaringan](https://developer.android.com/static/images/topic/architecture/data-layer/read-queue.png?authuser=8&hl=id)
Operasi tulis
Cara yang direkomendasikan untuk membaca data dalam aplikasi offline-first adalah menggunakan jenis observable, sedangkan untuk API tulis adalah API asinkron seperti fungsi penangguhan. Hal ini menghindari pemblokiran UI thread, dan membantu penanganan error karena operasi tulis di aplikasi offline-first dapat gagal saat melintasi batas jaringan.
interface UserDataRepository {
/**
* Updates the bookmarked status for a news resource
*/
suspend fun updateNewsResourceBookmark(newsResourceId: String, bookmarked: Boolean)
}
Dalam cuplikan di atas, pilihan API asinkron adalah Coroutine karena metode di atas ditangguhkan.
Strategi operasi tulis
Ketika menulis data di aplikasi offline-first, ada tiga strategi yang perlu dipertimbangkan. Pilihan Anda bergantung pada jenis data yang ditulis dan persyaratan aplikasi:
Operasi tulis khusus online
Cobalah untuk menulis data melintasi batas jaringan. Jika berhasil, update sumber data lokal. Jika tidak, berikan pengecualian dan biarkan pemanggil merespons dengan tepat.
![Operasi tulis khusus online](https://developer.android.com/static/images/topic/architecture/data-layer/write-online-only.png?authuser=8&hl=id)
Strategi ini sering digunakan untuk transaksi tulis yang harus terjadi secara online hampir secara real time. Misalnya, transfer bank. Karena operasi tulis mungkin gagal, sering kali perlu dikomunikasikan kepada pengguna bahwa operasi tulis gagal, atau cegah pengguna untuk mencoba menulis data sejak awal. Beberapa strategi yang dapat Anda gunakan dalam skenario ini dapat mencakup:
- Jika aplikasi memerlukan akses internet untuk menulis data, aplikasi dapat memilih untuk tidak menampilkan UI kepada pengguna yang memungkinkan pengguna menulis data, atau setidaknya menonaktifkannya.
- Anda dapat menggunakan pesan pop-up yang tidak dapat ditutup oleh pengguna, atau perintah sementara, untuk memberi tahu pengguna bahwa mereka sedang offline.
Operasi tulis yang diantrekan
Jika Anda memiliki objek yang ingin ditulis, masukkan ke antrean. Lanjutkan
untuk menghabiskan antrean dengan backoff eksponensial saat aplikasi kembali online. Di
Android, menghabiskan antrean offline adalah pekerjaan persisten yang sering didelegasikan ke
WorkManager
.
![Antrean operasi tulis dengan percobaan ulang](https://developer.android.com/static/images/topic/architecture/data-layer/write-backoff.png?authuser=8&hl=id)
Pendekatan ini adalah pilihan yang baik jika:
- Data tidak harus ditulis ke jaringan.
- Transaksi tidak peka terhadap waktu.
- Pengguna tidak perlu diberi tahu jika operasi gagal.
Kasus penggunaan untuk pendekatan ini meliputi peristiwa analisis dan logging.
Operasi tulis lambat
Tulis ke sumber data lokal terlebih dahulu, lalu antrekan operasi tulis tersebut untuk memberi tahu jaringan sesegera mungkin. Hal ini tidaklah mudah karena bisa terjadi konflik antara jaringan dan sumber data lokal saat aplikasi kembali online. Bagian berikutnya tentang resolusi konflik akan memberikan detail selengkapnya.
![Operasi tulis lambat dengan pemantauan jaringan](https://developer.android.com/static/images/topic/architecture/data-layer/write-queue.png?authuser=8&hl=id)
Pendekatan ini merupakan pilihan yang tepat saat data sangat dibutuhkan oleh aplikasi. Misalnya, dalam aplikasi daftar tugas offline-first, setiap tugas yang ditambahkan pengguna secara offline disimpan secara lokal untuk menghindari risiko kehilangan data.
Sinkronisasi dan resolusi konflik
Saat memulihkan konektivitasnya, aplikasi offline-first harus merekonsiliasi data di sumber data lokalnya dengan data di sumber data jaringan. Proses ini disebut sinkronisasi. Ada dua cara utama untuk menyinkronkan aplikasi dengan sumber data jaringannya:
- Sinkronisasi berbasis pull
- Sinkronisasi berbasis push
Sinkronisasi berbasis pull
Dalam sinkronisasi berbasis pull, aplikasi akan terhubung ke jaringan untuk membaca data aplikasi terbaru secara on demand. Heuristik umum untuk pendekatan ini adalah berbasis navigasi. Artinya, aplikasi hanya mengambil data tepat sebelum menampilkannya kepada pengguna.
Pendekatan ini berfungsi optimal jika aplikasi mengharapkan periode singkat hingga menengah tanpa konektivitas jaringan. Hal ini karena refresh data bersifat oportunistik, dan tidak adanya konektivitas dalam waktu lama meningkatkan peluang bahwa pengguna mencoba membuka tujuan aplikasi dengan cache yang sudah tidak berlaku atau kosong.
![Sinkronisasi berbasis pull](https://developer.android.com/static/images/topic/architecture/data-layer/pull-sync.png?authuser=8&hl=id)
Pertimbangkan aplikasi tempat token halaman digunakan untuk mengambil item dalam daftar scroll tanpa akhir untuk layar tertentu. Implementasi dapat menjangkau jaringan dengan lambat, mempertahankan data ke sumber data lokal, lalu membaca dari sumber data lokal untuk menampilkan informasi kembali kepada pengguna. Jika tidak ada konektivitas jaringan, repositori dapat meminta data dari sumber data lokal saja. Ini adalah pola yang digunakan oleh Library Paging Jetpack dengan RemoteMediator API-nya.
class FeedRepository(...) {
fun feedPagingSource(): PagingSource<FeedItem> { ... }
}
class FeedViewModel(
private val repository: FeedRepository
) : ViewModel() {
private val pager = Pager(
config = PagingConfig(
pageSize = NETWORK_PAGE_SIZE,
enablePlaceholders = false
),
remoteMediator = FeedRemoteMediator(...),
pagingSourceFactory = feedRepository::feedPagingSource
)
val feedPagingData = pager.flow
}
Kelebihan dan kekurangan sinkronisasi berbasis pull dirangkum dalam tabel di bawah:
Kelebihan | Kekurangan |
---|---|
Relatif mudah diterapkan. | Rentan dengan penggunaan data yang berat. Hal ini karena kunjungan berulang ke tujuan navigasi akan memicu pengambilan kembali informasi yang tidak perlu. Anda dapat melakukan mitigasi dengan penyimpanan cache yang sesuai. Hal ini dapat dilakukan di lapisan UI dengan operator cachedIn , atau di lapisan jaringan dengan cache HTTP. |
Data yang tidak diperlukan tidak akan pernah diambil. | Tidak diskalakan dengan baik dengan data relasional karena model yang di-pull harus dapat mengisi sendiri. Jika model yang disinkronkan bergantung pada model lain yang akan diambil untuk mengisi dirinya sendiri, masalah penggunaan data berat yang disebutkan sebelumnya akan menjadi lebih signifikan. Selain itu, hal ini dapat menyebabkan repositori model induk dan repositori model bertingkat saling bergantung. |
Sinkronisasi berbasis push
Dalam sinkronisasi berbasis push, sumber data lokal mencoba meniru set replika sumber data jaringan sebaik mungkin. Sumber data lokal secara proaktif mengambil jumlah data yang sesuai pada saat pertama kali dimulai untuk menetapkan dasar pengukuran, setelah itu mengandalkan notifikasi dari server untuk memperingatkannya saat data tersebut sudah tidak berlaku.
![Sinkronisasi berbasis push](https://developer.android.com/static/images/topic/architecture/data-layer/push-sync.png?authuser=8&hl=id)
Setelah menerima notifikasi yang sudah tidak berlaku, aplikasi menghubungi jaringan untuk
hanya mengupdate data yang ditandai sebagai tidak berlaku. Pekerjaan ini didelegasikan ke
Repository
yang menjangkau sumber data jaringan, dan menyimpan data
yang diambil ke sumber data lokal. Karena repositori mengekspos datanya dengan jenis observable, pembaca akan diberi tahu jika ada perubahan.
class UserDataRepository(...) {
suspend fun synchronize() {
val userData = networkDataSource.fetchUserData()
localDataSource.saveUserData(userData)
}
}
Dalam pendekatan ini, aplikasi menjadi semakin tidak bergantung pada sumber data jaringan dan dapat berfungsi tanpanya untuk jangka waktu yang lama. Pendekatan ini menawarkan akses baca dan tulis saat offline karena dianggap memiliki informasi terbaru dari sumber data jaringan secara lokal.
Kelebihan dan kekurangan sinkronisasi berbasis push dirangkum dalam tabel di bawah ini:
Kelebihan | Kekurangan |
---|---|
Aplikasi dapat tetap offline tanpa batas waktu. | Pembuatan versi data untuk resolusi konflik tidaklah mudah. |
Penggunaan data minimum. Aplikasi hanya mengambil data yang telah diubah. | Anda perlu mempertimbangkan masalah operasi tulis selama sinkronisasi. |
Berfungsi baik untuk data relasional. Setiap repositori hanya bertanggung jawab mengambil data untuk model yang didukungnya. | Sumber data jaringan harus mendukung sinkronisasi. |
Sinkronisasi campuran
Beberapa aplikasi menggunakan pendekatan campuran yang berbasis pull atau push, bergantung pada data. Misalnya, aplikasi media sosial dapat menggunakan sinkronisasi berbasis pull untuk mengambil feed "mengikuti" sesuai permintaan pengguna karena tingginya frekuensi update feed. Aplikasi yang sama dapat memilih untuk menggunakan sinkronisasi berbasis push untuk data tentang pengguna yang login, termasuk nama pengguna, foto profil, dan sebagainya.
Pada akhirnya, pilihan sinkronisasi offline-first bergantung pada persyaratan produk dan infrastruktur teknis yang tersedia.
Resolusi konflik
Jika aplikasi menulis data secara lokal saat offline namun tidak sesuai dengan sumber data jaringan, konflik yang terjadi harus Anda selesaikan sebelum sinkronisasi dapat dilakukan.
Resolusi konflik sering kali memerlukan pembuatan versi. Aplikasi perlu melakukan beberapa pembukuan untuk mencatat kapan perubahan terjadi. Hal ini memungkinkan penerusan metadata ke sumber data jaringan. Sumber data jaringan memiliki tanggung jawab untuk menyediakan sumber tepercaya absolut. Ada beragam strategi yang dapat dipertimbangkan untuk menyelesaikan konflik, bergantung pada kebutuhan aplikasi. Untuk aplikasi seluler, pendekatan yang umum digunakan adalah "last write wins".
Last write wins
Dalam pendekatan ini, perangkat melampirkan metadata stempel waktu ke data yang ditulis ke jaringan. Saat menerima data, sumber data jaringan akan menghapus data yang lebih lama dari statusnya saat ini dan menerima yang lebih baru dari statusnya saat ini.
![Resolusi konflik last write wins](https://developer.android.com/static/images/topic/architecture/data-layer/last-write-wins.png?authuser=8&hl=id)
Di atas, kedua perangkat sedang offline dan awalnya disinkronkan dengan sumber data jaringan. Saat offline, kedua perangkat menulis data secara lokal dan mencatat waktu penulisan data. Saat kembali online dan menyinkronkan dengan sumber data jaringan, jaringan akan menyelesaikan konflik dengan menyimpan data dari perangkat B karena data tersebut ditulis paling baru.
WorkManager di aplikasi offline-first
Dalam strategi operasi baca dan tulis yang dibahas di atas, ada dua utilitas umum:
- Antrean
- Operasi baca: Digunakan untuk menunda operasi baca hingga konektivitas jaringan tersedia.
- Operasi tulis: Digunakan untuk menunda operasi tulis hingga konektivitas jaringan tersedia, dan mengantrekan ulang operasi tulis untuk percobaan ulang.
- Pemantau konektivitas jaringan
- Operasi baca: Digunakan sebagai sinyal untuk menghabiskan antrean operasi baca saat aplikasi terhubung dan untuk sinkronisasi
- Operasi tulis: Digunakan sebagai sinyal untuk menghabiskan antrean operasi tulis saat aplikasi terhubung dan untuk sinkronisasi
Kedua kasus tersebut adalah contoh pekerjaan persisten yang merupakan keunggulan dari WorkManager. Misalnya dalam aplikasi contoh Now in Android, WorkManager digunakan sebagai antrean operasi baca dan pemantauan jaringan saat menyinkronkan sumber data lokal. Saat start-up, aplikasi akan melakukan tindakan berikut:
- Mengantrekan pekerjaan sinkronisasi operasi baca untuk memastikan adanya kesetaraan antara sumber data lokal dan sumber data jaringan.
- Menghabiskan antrean sinkronisasi operasi baca dan memulai sinkronisasi saat aplikasi sedang online.
- Melakukan operasi baca dari sumber data jaringan menggunakan backoff eksponensial.
- Menyimpan hasil operasi baca ke sumber data lokal untuk menyelesaikan konflik yang mungkin terjadi.
- Mengekspos data dari sumber data lokal untuk digunakan oleh lapisan aplikasi lainnya.
Hal di atas diilustrasikan dalam diagram di bawah ini:
![Sinkronisasi data di aplikasi Now in Android](https://developer.android.com/static/images/topic/architecture/data-layer/nia-sync.png?authuser=8&hl=id)
Antrean pekerjaan sinkronisasi dengan WorkManager diikuti dengan
menentukannya sebagai pekerjaan unik dengan KEEP
ExistingWorkPolicy
:
class SyncInitializer : Initializer<Sync> {
override fun create(context: Context): Sync {
WorkManager.getInstance(context).apply {
// Queue sync on app startup and ensure only one
// sync worker runs at any time
enqueueUniqueWork(
SyncWorkName,
ExistingWorkPolicy.KEEP,
SyncWorker.startUpSyncWork()
)
}
return Sync
}
}
Dengan SyncWorker.startupSyncWork()
ditentukan sebagai berikut:
/**
Create a WorkRequest to call the SyncWorker using a DelegatingWorker.
This allows for dependency injection into the SyncWorker in a different
module than the app module without having to create a custom WorkManager
configuration.
*/
fun startUpSyncWork() = OneTimeWorkRequestBuilder<DelegatingWorker>()
// Run sync as expedited work if the app is able to.
// If not, it runs as regular work.
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setConstraints(SyncConstraints)
// Delegate to the SyncWorker.
.setInputData(SyncWorker::class.delegatedData())
.build()
val SyncConstraints
get() = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
Secara khusus, Constraints
yang ditentukan oleh SyncConstraints
mengharuskan
NetworkType
menjadi NetworkType.CONNECTED
. Artinya, elemen ini menunggu hingga
jaringan tersedia sebelum berjalan.
Setelah jaringan tersedia, Pekerja akan menghabiskan antrean pekerjaan unik
yang ditentukan oleh SyncWorkName
dengan mendelegasikan ke instance Repository
yang sesuai. Jika sinkronisasi gagal, metode doWork()
akan ditampilkan dengan
Result.retry()
. WorkManager akan otomatis mencoba ulang sinkronisasi dengan
backoff eksponensial. Jika tidak, metode ini akan menampilkan Result.success()
yang menyelesaikan
sinkronisasi.
class SyncWorker(...) : CoroutineWorker(appContext, workerParams), Synchronizer {
override suspend fun doWork(): Result = withContext(ioDispatcher) {
// First sync the repositories in parallel
val syncedSuccessfully = awaitAll(
async { topicRepository.sync() },
async { authorsRepository.sync() },
async { newsRepository.sync() },
).all { it }
if (syncedSuccessfully) Result.success()
else Result.retry()
}
}
Contoh
Contoh Google berikut menunjukkan aplikasi offline-first. Jelajahi untuk melihat panduan ini dalam praktik:
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Produksi Status UI
- Lapisan UI
- Lapisan data