State<T> dan
MutableState<T> Jetpack Compose adalah holder nilai tujuan umum yang didukung oleh
sistem snapshot Compose. Meskipun paling sering digunakan untuk mendorong rekomposisi
UI, sistem snapshot itu sendiri tidak terkait dengan rendering UI.
Dengan library AppState—Transform
dan StateStore—Anda dapat menyimpan, memperoleh, mengamati, dan mempertahankan
State Compose di seluruh ViewModel, pengelola, repositori, dan layanan
latar belakang.
Mengapa menggunakan Compose State di luar UI?
Saat Anda membaca State Compose di dalam blok pengamatan status—seperti composable UI, transform, atau derivedStateOf—runtime Compose memberikan dua manfaat utama di seluruh lapisan arsitektur Anda:
- Pelacakan dependensi otomatis dan dinamis: Membaca
.valuestatus (atau menggunakan delegasi propertiby) akan otomatis mendaftarkan status tersebut sebagai dependensi blok. Karena pelacakan bersifat dinamis, dependensi ditentukan saat runtime berdasarkan alur kode yang dieksekusi. Jika cabang bersyarat tidak diambil (misalnya, setelah pemeriksaanifataureturnawal), status di dalam cabang tersebut tidak dibaca dan perubahan pada status tersebut tidak memicu komputasi ulang yang tidak perlu. - Konsistensi dan perincian: Anda dapat mendeklarasikan properti
MutableStateyang terpisah dan terperinci, serta menggabungkan penulisan multi-properti dalamSnapshot.withMutableSnapshot. Update perantara diisolasi ke snapshot yang dapat diubah dan diterapkan secara atomik saat blok selesai, sehingga pengamat yang membaca di dalam snapshot tidak pernah melihat status perantara yang tidak konsisten dan hanya menghitung ulang saat properti tertentu yang mereka baca berubah. Tidak sepertiStateFlow, Anda tidak perlu memilih antara satuStateFlow<UiState>monolitik (yang membatalkan setiap kolektor saat ada kolom yang berubah) dan propertiStateFlowterpisah (yang tidak dapat diupdate bersama dalam satu transaksi atomik).
Memilih API status yang tepat di seluruh arsitektur Anda
Pilih cara menyimpan dan mempertahankan State Compose berdasarkan berapa lama suatu
state harus aktif dan komponen mana yang perlu mengaksesnya:
| Kategori negara bagian | Masa aktif dan cakupan | Contoh | API State Compose yang direkomendasikan |
|---|---|---|---|
| Status live | Selalu disimpan dalam memori; dikueri baru saat memulai dan tidak dipertahankan di seluruh penghentian proses. | Status koneksi jaringan, status koneksi Bluetooth, status UI sementara seperti pemilihan item. |
|
| Status UI sementara | Bertahan dari perubahan konfigurasi dan penghentian proses yang dimulai oleh sistem, tidak disimpan tanpa batas waktu; dicakupkan ke satu layar atau aktivitas. | Elemen UI yang diperluas atau diciutkan, posisi scroll, kueri penelusuran layar, atau tombol filter. |
|
| Status sesi sementara | Bertahan dari penghentian proses latar belakang, tidak disimpan tanpa batas waktu; dibagikan di seluruh aktivitas, layanan, atau widget. | Timer fokus aktif, keranjang checkout multi-langkah, atau draf formulir yang sedang berlangsung. |
|
| Status permanen | Disimpan di disk tanpa batas waktu. | Preferensi dan setelan pengguna (DataStore), data aplikasi terstruktur, dan galeri media (Room). | Flow DataStore atau Room yang dikumpulkan di dalam transform menggunakan collectAsState |
Menggunakan Status Compose di ViewModel
Di dalam ViewModel, Anda dapat mendeklarasikan input layar independen sebagai properti MutableState terperinci, mempertahankannya di seluruh penghentian proses dengan SavedStateHandle.saveable, mengupdate beberapa properti secara atomik dengan Snapshot.withMutableSnapshot, dan menggabungkannya dengan aliran data Flow repositori di dalam transform:
sealed interface TaskListState { data object Loading : TaskListState data class Success( val tasks: List<Task>, val selectedTaskIds: Set<String>, val isOnline: Boolean = true, ) : TaskListState } @HiltViewModel class TaskListViewModel @Inject constructor( private val tasksRepository: TasksRepository, savedStateHandle: SavedStateHandle, ) : ViewModel() { var selectedTaskIds by mutableStateOf(emptySet<String>()) private set var searchQuery by savedStateHandle.saveable { mutableStateOf("") } private set fun updateSearchQuery(query: String) { Snapshot.withMutableSnapshot { searchQuery = query selectedTaskIds = emptySet() } } val tasksState: State<TaskListState> = transform<TaskListState>( initialValue = TaskListState.Loading, scope = viewModelScope, ) { val tasks = remember(searchQuery) { tasksRepository.getTasks(searchQuery) }.collectAsState(initial = null).value ?: return@transform TaskListState.Loading TaskListState.Success( tasks = tasks, selectedTaskIds = selectedTaskIds, ) } }
Pola utama dalam ViewModel ini:
- Gunakan
SavedStateHandle.saveableuntuk status tersimpan reaktif: MendelegasikansearchQuerykeSavedStateHandle.saveabledenganmutableStateOf(dariandroidx.lifecycle:lifecycle-viewmodel-compose) akan mempertahankan nilai di seluruh penghentian proses sebagaiMutableState, sehinggatransformmelacak pembacaan secara otomatis. HindariSavedStateHandle.savedkosong, yang menampilkan properti Kotlin biasa, bukan ComposeStatedan tidak memicu rekomposisi saat diubah. - Penulisan multi-properti atomik dengan
Snapshot.withMutableSnapshot: MemperbaruisearchQuerydanselectedTaskIdsdi dalamSnapshot.withMutableSnapshotmengisolasi penulisan dan meng-commit kedua perubahan dalam satu transaksi snapshot atomik, sehingga observer (termasuktransform) melihat kedua update bersama-sama dalam satu penerusan rekomposisi, bukan mengamatisearchQuerybaru yang dipasangkan denganselectedTaskIdsyang sudah tidak berlaku. - Menggunakan kembali flow di seluruh rekomposisi dengan
remember(key): MenggabungkantasksRepository.getTasks(searchQuery)dalamrememberdengansearchQuerysebagai kunci akan memastikan bahwa saatselectedTaskIdsberubah dan merekomposisi bloktransform, Compose akan menggunakan kembaliFlowdatabase yang ada, bukan memulai ulang kueri.
Membagikan status sistem live di seluruh aplikasi Anda
Untuk status sistem aktif yang dibagikan di beberapa ViewModel, aktivitas, atau
layanan—seperti status koneksi jaringan atau Bluetooth—Anda tidak ingin
mempertahankan status usang ke disk di seluruh penghentian proses. Sebagai gantinya, buat kueri status sistem saat ini saat memulai, simpan dalam properti mutableStateOf pribadi di dalam singleton cakupan aplikasi, dan ekspos State<T> hanya baca:
interface NetworkMonitor { val isOnline: State<Boolean> } @Singleton class LiveNetworkMonitor @Inject constructor( @ApplicationContext context: Context, ) : NetworkMonitor { private val connectivityManager = context.getSystemService(ConnectivityManager::class.java) private val _isOnline = mutableStateOf(checkCurrentConnectivity()) override val isOnline: State<Boolean> = _isOnline private val callback = object : ConnectivityManager.NetworkCallback() { override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities, ) { _isOnline.value = networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_INTERNET ) } override fun onLost(network: Network) { _isOnline.value = false } } init { connectivityManager.registerDefaultNetworkCallback(callback) } private fun checkCurrentConnectivity(): Boolean { val network = connectivityManager.activeNetwork ?: return false val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false return capabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_INTERNET ) } }
MutableState mendukung pembacaan dan update yang konsisten dari thread mana pun, sehingga
NetworkCallback dapat mengupdate _isOnline.value langsung dari thread callback
latar belakangnya. Jika callback latar belakang memperbarui beberapa properti MutableState
secara bersamaan, gabungkan penulisan dalam
Snapshot.withMutableSnapshot agar diterapkan
secara atomik.
Komponen apa pun di aplikasi Anda dapat membaca networkMonitor.isOnline.value—baik sebagai
pemeriksaan satu kali, di dalam composable UI, atau di dalam blok pengamatan status seperti
listener, derivedStateOf, atau
transform. Misalnya, TaskListViewModel dapat membaca
networkMonitor.isOnline.value di dalam blok transform-nya:
val tasksState: State<TaskListState> = transform<TaskListState>( initialValue = TaskListState.Loading, scope = viewModelScope, ) { val tasks = remember(searchQuery) { tasksRepository.getTasks(searchQuery) }.collectAsState(initial = null).value ?: return@transform TaskListState.Loading TaskListState.Success( tasks = tasks, selectedTaskIds = selectedTaskIds, isOnline = networkMonitor.isOnline.value, ) }
Karena transform melacak networkMonitor.isOnline.value bersama dengan
searchQuery, selectedTaskIds, dan repositori Flow, setiap perubahan pada
konektivitas akan otomatis menghitung ulang TaskListState.Success tanpa
membuat kueri ulang database.