Bergantung pada tempat status Anda diangkat dan logika yang diperlukan, Anda dapat menggunakan API yang berbeda untuk menyimpan dan memulihkan status UI. Setiap aplikasi menggunakan kombinasi API untuk mencapai hal ini.
Aplikasi Android apa pun dapat kehilangan status UI karena pembuatan ulang aktivitas atau proses. Hilangnya status ini dapat terjadi karena peristiwa berikut:
- Perubahan konfigurasi. Aktivitas akan dihapus dan dibuat ulang kecuali jika perubahan konfigurasi ditangani secara manual.
- Penghentian proses yang dimulai oleh sistem. Aplikasi berada di latar belakang dan perangkat membebaskan resource (seperti memori) untuk digunakan oleh proses lain.
Mempertahankan status setelah peristiwa ini penting untuk pengalaman pengguna yang positif. Memilih status mana yang akan dipertahankan bergantung pada alur pengguna unik aplikasi Anda. Sebagai praktik terbaik, Anda setidaknya harus mempertahankan input pengguna dan status terkait navigasi. Contohnya mencakup posisi scroll daftar, ID item yang diinginkan pengguna agar lebih detail, pemilihan preferensi pengguna yang sedang berlangsung, atau input dalam kolom teks.
Halaman ini merangkum API yang tersedia untuk menyimpan status UI bergantung pada tempat status Anda diangkat dan logika yang membutuhkannya.
Logika UI
Jika status Anda diangkat di UI, baik dalam fungsi composable atau class holder status
biasa yang mencakup Komposisi, Anda dapat menggunakan
rememberSaveable
untuk mempertahankan status pada seluruh pembuatan ulang aktivitas dan proses.
Dalam cuplikan berikut, rememberSaveable
digunakan untuk menyimpan satu status
elemen UI boolean:
@Composable fun ChatBubble( message: Message ) { var showDetails by rememberSaveable { mutableStateOf(false) } ClickableText( text = AnnotatedString(message.content), onClick = { showDetails = !showDetails } ) if (showDetails) { Text(message.timestamp) } }
showDetails
adalah variabel boolean yang menyimpan apakah balon chat diciutkan
atau diluaskan.
rememberSaveable
menyimpan status elemen UI dalam Bundle
melalui
mekanisme status instance yang disimpan.
Mekanisme ini dapat menyimpan jenis primitif ke paket secara otomatis. Jika status
disimpan di jenis yang tidak primitif, seperti class data, Anda dapat menggunakan
mekanisme penyimpanan yang berbeda, seperti menggunakan anotasi Parcelize
,
menggunakan Compose API seperti listSaver
, dan mapSaver
, atau menerapkan
class saver kustom yang memperluas class Saver
runtime Compose. Lihat dokumentasi Cara
menyimpan status untuk mempelajari metode ini lebih lanjut.
Dalam cuplikan berikut, Compose API rememberLazyListState
menyimpan LazyListState
, yang terdiri dari status scroll
LazyColumn
atau LazyRow
, menggunakan rememberSaveable
. Fungsi ini menggunakan
LazyListState.Saver
, yang merupakan saver kustom yang dapat
menyimpan dan memulihkan status scroll. Setelah pembuatan ulang aktivitas atau proses (misalnya,
setelah perubahan konfigurasi seperti mengubah orientasi perangkat),
status scroll dipertahankan.
@Composable fun rememberLazyListState( initialFirstVisibleItemIndex: Int = 0, initialFirstVisibleItemScrollOffset: Int = 0 ): LazyListState { return rememberSaveable(saver = LazyListState.Saver) { LazyListState( initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset ) } }
Praktik terbaik
rememberSaveable
menggunakan Bundle
untuk menyimpan status UI, yang dibagikan oleh
API lain yang juga menulis ke UI tersebut, seperti panggilan onSaveInstanceState()
dalam
aktivitas Anda. Namun, ukuran Bundle
ini terbatas, dan menyimpan objek
besar dapat menyebabkan pengecualian TransactionTooLarge
di runtime. Hal ini
dapat menyebabkan masalah khusus di satu aplikasi Activity
saat
Bundle
yang sama digunakan di seluruh aplikasi.
Untuk menghindari jenis error ini, Anda tidak boleh menyimpan objek kompleks besar atau daftar objek kompleks dalam paket.
Sebagai gantinya, simpan status minimum yang diperlukan, seperti ID atau kunci, dan gunakan status ini untuk mendelegasikan pemulihan status UI yang lebih kompleks ke mekanisme lain, seperti penyimpanan persisten.
Pilihan desain ini bergantung pada kasus penggunaan tertentu untuk aplikasi Anda dan perilaku yang diharapkan pengguna.
Memverifikasi pemulihan status
Anda dapat memverifikasi bahwa status yang disimpan dengan rememberSaveable
di
elemen Compose dipulihkan dengan benar saat aktivitas atau proses
dibuat ulang. Ada API khusus untuk mencapai hal ini, seperti
StateRestorationTester
. Lihat dokumentasi Pengujian untuk
mempelajari lebih lanjut.
Logika bisnis
Jika status elemen UI diangkat ke ViewModel
karena diperlukan oleh logika bisnis, Anda dapat menggunakan API ViewModel
.
Salah satu manfaat utama penggunaan ViewModel
di aplikasi Android adalah
API ini menangani perubahan konfigurasi secara gratis. Saat ada perubahan
konfigurasi, dan aktivitas dihapus dan dibuat ulang, status UI yang diangkat ke
ViewModel
disimpan di memori. Setelah pembuatan ulang, instance ViewModel
lama akan dilampirkan ke instance aktivitas baru.
Namun, instance ViewModel
tidak bertahan dari penghentian proses yang dimulai oleh sistem.
Agar status UI bertahan dari penghentian proses, gunakan modul Status Tersimpan untuk
ViewModel, yang berisi SavedStateHandle
API.
Praktik terbaik
SavedStateHandle
juga menggunakan mekanisme Bundle
untuk menyimpan status UI, sehingga
Anda hanya boleh menggunakannya untuk menyimpan status elemen UI sederhana.
Status UI Layar, yang dibuat dengan menerapkan aturan bisnis dan mengakses
lapisan aplikasi selain UI, tidak boleh disimpan di
SavedStateHandle
karena adanya potensi kompleksitas dan ukuran. Anda dapat menggunakan
mekanisme yang berbeda untuk menyimpan data kompleks atau besar, seperti penyimpanan
persisten lokal. Setelah pembuatan ulang proses, layar akan dibuat ulang dengan
status sementara yang dipulihkan, yang disimpan di SavedStateHandle
(jika ada), dan
status UI layar akan dibuat kembali dari lapisan data.
SavedStateHandle
API
SavedStateHandle
memiliki API yang berbeda untuk menyimpan status elemen UI,
terutama:
Compose State |
saveable() |
---|---|
StateFlow |
getStateFlow() |
Compose State
Gunakan saveable
API dari SavedStateHandle
untuk membaca dan menulis status
elemen UI sebagai MutableState
, sehingga bertahan dari pembuatan ulang aktivitas dan proses dengan
penyiapan kode minimal.
saveable
API langsung mendukung jenis primitif dan menerima parameter
stateSaver
untuk menggunakan saver kustom, seperti rememberSaveable()
.
Dalam cuplikan berikut, message
menyimpan jenis input pengguna ke dalam
TextField
:
class ConversationViewModel( savedStateHandle: SavedStateHandle ) : ViewModel() { var message by savedStateHandle.saveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) } private set fun update(newMessage: TextFieldValue) { message = newMessage } /*...*/ } val viewModel = ConversationViewModel(SavedStateHandle()) @Composable fun UserInput(/*...*/) { TextField( value = viewModel.message, onValueChange = { viewModel.update(it) } ) }
Lihat dokumentasi SavedStateHandle
untuk informasi selengkapnya tentang
penggunaan saveable
API.
StateFlow
Gunakan getStateFlow()
untuk menyimpan status elemen UI dan menggunakannya sebagai flow
dari SavedStateHandle
. StateFlow
bersifat
hanya baca, dan API mengharuskan Anda menentukan kunci agar Anda dapat mengganti flow
untuk mengeluarkan nilai baru. Dengan kunci yang dikonfigurasi, Anda dapat mengambil StateFlow
dan mengumpulkan nilai terbaru.
Dalam cuplikan berikut, savedFilterType
adalah variabel StateFlow
yang
menyimpan jenis filter yang diterapkan ke daftar saluran chat di aplikasi chat:
private const val CHANNEL_FILTER_SAVED_STATE_KEY = "ChannelFilterKey" class ChannelViewModel( channelsRepository: ChannelsRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val savedFilterType: StateFlow<ChannelsFilterType> = savedStateHandle.getStateFlow( key = CHANNEL_FILTER_SAVED_STATE_KEY, initialValue = ChannelsFilterType.ALL_CHANNELS ) private val filteredChannels: Flow<List<Channel>> = combine(channelsRepository.getAll(), savedFilterType) { channels, type -> filter(channels, type) }.onStart { emit(emptyList()) } fun setFiltering(requestType: ChannelsFilterType) { savedStateHandle[CHANNEL_FILTER_SAVED_STATE_KEY] = requestType } /*...*/ } enum class ChannelsFilterType { ALL_CHANNELS, RECENT_CHANNELS, ARCHIVED_CHANNELS }
Setiap kali pengguna memilih jenis filter baru, setFiltering
akan dipanggil. Tindakan ini
menyimpan nilai baru dalam SavedStateHandle
yang disimpan dengan kunci
_CHANNEL_FILTER_SAVED_STATE_KEY_
. savedFilterType
adalah flow yang mengeluarkan
nilai terbaru yang disimpan ke kunci. filteredChannels
berlangganan ke flow untuk
melakukan pemfilteran saluran.
Lihat dokumentasi SavedStateHandle
untuk informasi selengkapnya tentang
getStateFlow()
API.
Ringkasan
Tabel berikut merangkum API yang dibahas di bagian ini, dan kapan API tersebut digunakan menyimpan status UI:
Peristiwa | Logika UI | Logika bisnis dalam ViewModel |
---|---|---|
Perubahan konfigurasi | rememberSaveable |
Otomatis |
Penghentian proses yang dimulai oleh sistem | rememberSaveable |
SavedStateHandle |
API yang akan digunakan bergantung pada tempat status disimpan dan logika yang
diperlukan. Untuk status yang digunakan dalam logika UI, gunakan rememberSaveable
. Untuk
status yang digunakan dalam logika bisnis, jika Anda menyimpannya dalam ViewModel
,
simpan menggunakan SavedStateHandle
.
Anda harus menggunakan API paket (rememberSaveable
dan SavedStateHandle
) untuk
menyimpan sejumlah kecil status UI. Data ini adalah data minimum yang diperlukan untuk memulihkan
UI ke status sebelumnya, beserta mekanisme penyimpanan lainnya. Misalnya,
jika Anda menyimpan ID profil yang dilihat pengguna dalam paket,
Anda dapat mengambil data berat, seperti detail profil, dari lapisan data.
Untuk informasi selengkapnya tentang berbagai cara menyimpan status UI, lihat dokumentasi Menyimpan dokumentasi Status UI umum dan halaman lapisan data dalam panduan arsitektur.
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Tempat mengangkat status
- Status dan Jetpack Compose
- Daftar dan petak