Kullanıcı arayüzü durumunu Compose'da kaydet

Eyaletinizin çekildiği yere ve gerekli mantığa bağlı olarak, kullanıcı arayüzü durumunuzu depolamak ve geri yüklemek için farklı API'ler kullanabilirsiniz. Her uygulama bunu en iyi şekilde gerçekleştirmek için bir API kombinasyonu kullanır.

Herhangi bir Android uygulaması, etkinlik veya işlem yeniden oluşturma nedeniyle kullanıcı arayüzü durumunu kaybedebilir. Bu durum kaybı aşağıdaki olaylardan kaynaklanabilir:

Bu etkinliklerden sonraki durumun korunması, olumlu bir kullanıcı deneyimi için esastır. Kalıcı olacak durumu seçmek, uygulamanızın benzersiz kullanıcı akışlarına bağlıdır. En iyi uygulama olarak, en azından kullanıcı girişini ve gezinmeyle ilgili durumu korumalısınız. Örneğin, bir listenin kaydırma konumu, kullanıcının daha fazla bilgi edinmek istediği öğenin kimliği, kullanıcı tercihlerinin seçimi veya metin alanlarına giriş işlemleri bu kapsama girer.

Bu sayfada, eyaletinizin nereye çekildiğine ve buna ihtiyaç duyulan mantığa bağlı olarak kullanıcı arayüzü durumunu depolamak için kullanılabilecek API'ler özetlenmektedir.

Kullanıcı arayüzü mantığı

Eyaletiniz kullanıcı arayüzünde composable Functions'ta veya Beste kapsamındaki sade durum sahibi sınıflarında bulunuyorsa etkinlik ve süreç yeniden oluşturma genelinde durumu korumak için rememberSaveable aracını kullanabilirsiniz.

Aşağıdaki snippet'te rememberSaveable, tek bir boole kullanıcı arayüzü öğesi durumunu depolamak için kullanılmıştır:

@Composable
fun ChatBubble(
    message: Message
) {
    var showDetails by rememberSaveable { mutableStateOf(false) }

    ClickableText(
        text = AnnotatedString(message.content),
        onClick = { showDetails = !showDetails }
    )

    if (showDetails) {
        Text(message.timestamp)
    }
}

Şekil 1. Sohbet mesajı baloncuğu dokunulduğunda genişler ve daralır.

showDetails, sohbet balonunun daraltılmış veya genişletilmiş halini saklayan bir boole değişkenidir.

rememberSaveable, kullanıcı arayüzü öğesinin durumunu kayıtlı örnek durum mekanizması aracılığıyla bir Bundle içinde depolar.

Temel türleri pakete otomatik olarak depolayabilir. Durumunuz, veri sınıfı gibi temel olmayan bir türde bulunuyorsa Parcelize ek açıklamasını kullanma, listSaver ve mapSaver gibi Oluşturma API'lerini kullanma veya Compose çalışma zamanı Saver sınıfını genişleten özel tasarruf sınıfı uygulama gibi farklı depolama mekanizmaları kullanabilirsiniz. Bu yöntemler hakkında daha fazla bilgi edinmek için Durumu depolama yöntemleri belgelerine bakın.

Aşağıdaki snippet'te rememberSaveable kullanılan LazyColumn veya LazyRow kaydırma durumundan oluşan rememberLazyListState Compose API'leri LazyListState depolar. Kaydırma durumunu saklayıp geri yükleyebilen özel bir koruyucu olan LazyListState.Saver kullanır. Bir etkinlik veya işlem yeniden oluşturulduktan sonra (örneğin, cihaz yönünü değiştirme gibi bir yapılandırma değişikliğinden sonra) kaydırma durumu korunur.

@Composable
fun rememberLazyListState(
    initialFirstVisibleItemIndex: Int = 0,
    initialFirstVisibleItemScrollOffset: Int = 0
): LazyListState {
    return rememberSaveable(saver = LazyListState.Saver) {
        LazyListState(
            initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset
        )
    }
}

En iyi uygulama

rememberSaveable, kullanıcı arayüzü durumunu depolamak için bir Bundle kullanır. Bu bilgi, etkinliğinizdeki onSaveInstanceState() çağrıları gibi kendisine veri yazan diğer API'ler tarafından da paylaşılır. Bununla birlikte, bu Bundle öğesinin boyutu sınırlıdır ve büyük nesnelerin depolanması, çalışma zamanında TransactionTooLarge istisnalarına yol açabilir. Bu, özellikle aynı Bundle öğesinin uygulama genelinde kullanıldığı tekil Activity uygulamalarında sorunlu olabilir.

Bu tür kilitlenmeleri önlemek için karmaşık nesneleri veya nesne listelerini pakette depolamamalısınız.

Bunun yerine, kimlikler veya anahtarlar gibi gereken minimum durumu depolayın ve bunları, daha karmaşık kullanıcı arayüzü durumlarını kalıcı depolama gibi diğer mekanizmalara geri yükleme yetkisi vermek için kullanın.

Bu tasarım seçenekleri, uygulamanızın belirli kullanım alanlarına ve kullanıcıların uygulamanızın nasıl davranmasını beklediğine bağlıdır.

Eyalet geri yüklemesini doğrulama

Etkinlik veya işlem yeniden oluşturulduğunda, E-posta Yaz öğelerinizde rememberSaveable ile depolanan durumun doğru şekilde geri yüklendiğini doğrulayabilirsiniz. Bunu başarmak için, StateRestorationTester gibi belirli API'ler vardır. Daha fazla bilgi edinmek için Test belgelerine bakın.

İş mantığı

Kullanıcı arayüzü öğenizin durumu, iş mantığının gerektirdiği için ViewModel değerine ayarlanmışsa ViewModel API'lerini kullanabilirsiniz.

Android uygulamanızda ViewModel kullanmanın temel avantajlarından biri, yapılandırma değişikliklerini ücretsiz olarak işlemesidir. Bir yapılandırma değişikliği olduğunda etkinlik kaldırılıp yeniden oluşturulduğunda ViewModel öğesine taşınan kullanıcı arayüzü durumu bellekte tutulur. Yeniden oluşturma işleminden sonra eski ViewModel örneği, yeni etkinlik örneğine eklenir.

Ancak ViewModel örneği, sistem tarafından başlatılan işlem ölümünden sonra kurtarılamaz. Kullanıcı arayüzü durumunun bu durumu aşmasını sağlamak için SavedStateHandle API'yi içeren ViewModel için Kayıtlı Durum modülünü kullanın.

En iyi uygulama

SavedStateHandle, kullanıcı arayüzü durumunu depolamak için de Bundle mekanizmasını kullanır. Bu nedenle, bunu yalnızca basit kullanıcı arayüzü öğesi durumunu depolamak için kullanmalısınız.

İş kuralları uygulanarak ve uygulamanızın kullanıcı arayüzü dışındaki katmanlarına erişilerek oluşturulan ekran kullanıcı arayüzü durumu, olası karmaşıklığı ve boyutundan dolayı SavedStateHandle içinde depolanmamalıdır. Karmaşık veya büyük verileri depolamak için yerel kalıcı depolama gibi farklı mekanizmalar kullanabilirsiniz. İşlemin yeniden oluşturulmasından sonra ekran, SavedStateHandle içinde depolanan geri yüklenmiş geçici durumla (varsa) yeniden oluşturulur ve ekran kullanıcı arayüzü durumu da veri katmanından tekrar üretilir.

SavedStateHandle API

SavedStateHandle, kullanıcı arayüzü öğesi durumunu depolamak için farklı API'lere sahiptir. En önemlisi şudur:

Oluştur State saveable()
StateFlow getStateFlow()

State oluştur

Kullanıcı arayüzü öğesi durumunu MutableState olarak okuyup yazmak için SavedStateHandle saveable API'sini kullanın. Böylece minimum kod kurulumuyla etkinlikten ve süreç yeniden oluşturmada hayatta kalır.

saveable API, kullanıma hazır temel türleri destekler ve rememberSaveable() gibi özel koruyucuları kullanmak için bir stateSaver parametresi alır.

Aşağıdaki snippet'te message, kullanıcı girişi türlerini bir TextField öğesine depolar:

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) }
    )
}

saveable API'yi kullanma hakkında daha fazla bilgi için SavedStateHandle belgelerine bakın.

StateFlow

Kullanıcı arayüzü öğesi durumunu depolamak ve SavedStateHandle'dan akış olarak tüketmek için getStateFlow() kullanın. StateFlow salt okunurdur ve API, akışı yeni bir değer yayınlayacak şekilde değiştirebilmeniz için bir anahtar belirtmenizi gerektirir. Yapılandırdığınız anahtarla StateFlow öğesini alabilir ve en son değeri toplayabilirsiniz.

Aşağıdaki snippet'te savedFilterType, bir sohbet uygulamasındaki sohbet kanalları listesine uygulanan bir filtre türünü depolayan StateFlow değişkenidir:

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
}

Kullanıcının yeni bir filtre türü seçtiğinde setFiltering çağrılır. Bu işlem, _CHANNEL_FILTER_SAVED_STATE_KEY_ anahtarıyla depolanan SavedStateHandle alanına yeni bir değer kaydeder. savedFilterType, anahtarda depolanan en son değeri yayınlayan bir akıştır. filteredChannels, kanal filtrelemesi gerçekleştirmek için akışa abone olur.

getStateFlow() API hakkında daha fazla bilgi için SavedStateHandle belgelerine bakın.

Özet

Aşağıdaki tabloda, bu bölümde ele alınan API'ler ve kullanıcı arayüzü durumunu kaydetmek için her birinin ne zaman kullanılacağı özetlenmiştir:

Etkinlik Kullanıcı arayüzü mantığı ViewModel konumunda iş mantığı
Yapılandırma değişiklikleri rememberSaveable Otomatik
Sistem tarafından başlatılan işlem ölümü rememberSaveable SavedStateHandle

Kullanılacak API, durumun nerede tutulduğuna ve gerektirdiği mantığa bağlıdır. Kullanıcı arayüzü mantığında kullanılan durum için rememberSaveable kullanın. İş mantığında kullanılan eyalet için ViewModel içinde tutuyorsanız SavedStateHandle kullanarak kaydedin.

Az miktarda kullanıcı arayüzü durumu depolamak için paket API'lerini (rememberSaveable ve SavedStateHandle) kullanmanız gerekir. Bu veri, kullanıcı arayüzünü diğer depolama mekanizmalarıyla birlikte önceki durumuna geri yüklemek için gereken minimum değerdir. Örneğin, kullanıcının pakette baktığı bir profilin kimliğini depolarsanız profil ayrıntıları gibi ağır verileri veri katmanından getirebilirsiniz.

Kullanıcı arayüzü durumunu kaydetmenin farklı yolları hakkında daha fazla bilgi için genel Kullanıcı Arayüzü Durumunu Kaydetme belgelerine ve mimari kılavuzunun veri katmanı sayfasına bakın.