remember — ключови понятия, запазване на стойности между рекомпозициите

Автор: IT Sectr Публикувано: 2026-06-28 Време за четене: 8 мин

remember — е функция в Jetpack Compose, която позволява запазване на стойност между рекомпозициите, предотвратявайки нулиране на състоянието при всяко рестартиране на Composable функция. Без remember всички променливи, декларирани вътре във функцията, биха се нулирали при всяка актуализация на UI, правейки състоянието нестабилно и безполезно. Според Google Android Developers, 2026, правилното използване на remember е основата на коректното управление на състоянието в декларативния UI.

Основни точки

  • remember запазва стойността в слота на композицията между рекомпозициите на функцията
  • Ключове в remember(key) определят кога стойността трябва да бъде преизчислена
  • rememberSaveable запазва стойността при завъртане на екрана и минимизиране на приложението
  • derivedStateOf създава производно състояние, оптимизирайки изчисленията при рекомпозиция
  • Нулиране на remember става само когато функцията напусне композицията

Какво е remember в Jetpack Compose

remember — е вградена функция на Jetpack Compose от пакета androidx.compose.runtime, която създава обект, запазващ стойността си между последователни извиквания на Composable функция. Технически, remember работи със слотове — специални клетки памет, разпределени за всяка позиция на извикване на функцията в UI дървото. Докато функцията остава в композицията (не е премахната чрез if условие), remember ще връща запазената стойност при всяка рекомпозиция.

Синтаксисът на remember е прост: във фигурни скоби се посочва блокът за изчисление на началната стойност. Блокът се изпълнява само веднъж — при първата композиция на функцията. Всички следващи рекомпозиции връщат вече запазената стойност, без да извикват блока отново. Ако обаче е посочен ключ (параметър key), блокът се изпълнява отново при промяна на ключа, което позволява преизчисляване на стойността въз основа на входните данни.

remember не е магия на Kotlin — това е проста функция с параметър key и ламбда за изчисление. Вътрешната ѝ реализация използва Composer за четене и запис в слотовете. Компилаторът на Kotlin, виждайки извикване на remember, генерира код, който достъпва текущия CompositionContext и управлява слотовете. Това означава, че remember може да бъде извикван само вътре в Composable функция или друга функция в контекста на композицията.

Видове remember: remember, rememberSaveable, derivedStateOf

В Jetpack Compose съществуват три основни варианта на remember, всеки със свое предназначение. Базовият remember запазва стойността само в паметта на текущия процес — при завъртане на екрана (config change) Compose пресъздава Composition и всички remember стойности се нулират. За запазване на данни при конфигурационни промени и спиране на процеса се използва rememberSaveable.

rememberSaveable сериализира стойността в Bundle чрез механизма SavedStateHandle или Parcelable. Това позволява оцеляване при завъртане на екрана, извикване на „Не запазвай активности” и дори временно минимизиране на приложението. Въпреки това, rememberSaveable налага ограничения върху типа на запазваните данни: те трябва да бъдат примитиви, Parcelable, Serializable или да поддържат Saver конвертор.

derivedStateOf — не е механизъм за запазване, а оптимизация. Създава State, чиято стойност се изчислява на базата на други State обекти. derivedStateOf реагира на промени в изходните състояния, но преизчислява стойността само когато има абонати. Ако текущата рекомпозиция не чете derivedStateOf, изчислението не се изпълнява, което спестява ресурси при чести, но ненужни актуализации.

kotlin
@Composable
fun RememberVariants() {
    // 1. Основен remember: продължава до напускане на композицията
    val createdAt = remember { System.currentTimeMillis() }

    // 2. rememberSaveable: оцелява при завъртане на екрана
    var username by rememberSaveable { mutableStateOf("") }

    // 3. derivedStateOf: изчислява се само когато е необходимо
    val isButtonVisible = remember {
        derivedStateOf { username.length() > 3 }
    }

    Text("Създадено: $createdAt")
    TextField(value = username, onValueChange = { username = it })

    if (isButtonVisible.value) {
        Text("Бутонът ще бъде показан")
    }
}

remember vs rememberSaveable: кога какво да използваме

Изборът между remember и rememberSaveable зависи от това колко критично е запазването на данни при конфигурационни промени. За временни състояния, чиято загуба при завъртане не е важна (анимация, текуща позиция на превъртане, състояние на фокус) — достатъчно е обикновеното remember. За критични данни (текст във форма, избрани елементи, квадратчета за отметка) — използвайте rememberSaveable.

Производителност на rememberSaveable е по-ниска от обикновеното remember, тъй като изисква сериализация в Bundle. Използвайте rememberSaveable само за данни, които наистина трябва да оцелеят при пресъздаване на Activity. За всичко останало — обикновено remember. Прекомерната употреба на rememberSaveable води до забавяне при завъртания на екрана и превключване между приложения.

Ако работите с класове, които не поддържат Parcelable или Serializable, използвайте Saver — конвертор, който определя как да запазите и възстановите обект. Saver се описва от двойка ламбди: save (преобразува обекта в запазваем тип) и restore (възстановява обекта от запазените данни). Стандартният Saver вече е имплементиран за mutableStateListOf и mutableStateMapOf.

ХарактеристикаrememberrememberSaveable
Запазване при рекомпозицияДаДа
Запазване при завъртанеНеДа
Запазване при спиране на процесаНеДа
Изисквания към типаВсякакъвParcelable, Serializable, Saver
ПроизводителностВисокаСредна

Примери за използване на remember в реални проекти

Нека разгледаме типичен сценарий — екран за редактиране на профил, където remember се използва за няколко цели: съхраняване на състоянието на полетата на формуляра, изчисляване на производни стойности и кеширане на изчислително скъпи операции.

kotlin
data class ProfileUiState(
    val name: String = "",
    val bio: String = "",
    val isSaving: Boolean = false
)

@Composable
fun ProfileEditor() {
    var state by rememberSaveable { mutableStateOf(ProfileUiState()) }
    val isValid = remember {
        derivedStateOf { state.name.isNotBlank() && state.bio.length() <= 500 }
    }

    val bioWarning = remember(state.bio) {
        if (state.bio.length() > 400) {
            "${500 - state.bio.length} chars left"
        } else null
    }

    Column(modifier = Modifier.padding(16.dp)) {
        OutlinedTextField(
            value = state.name,
            onValueChange = { state = state.copy(name = it) },
            label = { Text("Име") }
        )

        OutlinedTextField(
            value = state.bio,
            onValueChange = { state = state.copy(bio = it) },
            label = { Text("Относно") }
        )

        bioWarning?.let { Text(it, color = MaterialTheme.colorScheme.error) }

        Button(onClick = { /* save */ },
            enabled = isValid.value) {
            Text("Запазване")
        }
    }
}

В примера състоянието се запазва чрез rememberSaveable — при завъртане на екрана текстът няма да изчезне. isValid се изчислява чрез derivedStateOf, което предотвратява ненужни изчисления при рекомпозиции. bioWarning използва remember с ключ bio — това е скъпо изчисление (по избор, за демонстрация), което се преизчислява само при промяна на bio, а не при всяка рекомпозиция.

Производни състояния и remember с ключове

Производните състояния (derived state) са стойности, които се изчисляват от други State обекти. Вместо да ги изчислявате при всяка рекомпозиция и да пилеете CPU за идентични резултати, remember с derivedStateOf изчислява стойността само при промяна на източниците. Това е особено полезно за филтриране, сортиране и агрегиране на данни.

remember с ключове (remember(key) { calculation }) — друг механизъм за оптимизация. Ако ключът не се е променил от предишната рекомпозиция, блокът за изчисление не се изпълнява и се връща кешираната стойност. Това е удобно за кеширане на обекти, чието създаване е скъпо: форматиране на дати, парсване на JSON, създаване на големи неизменяеми колекции.

kotlin
@Composable
fun SearchResults(allItems: List<Item>, query: String) {
    // derivedStateOf: филтърът се преизчислява само когато входните данни се променят
    val filtered = remember {
        derivedStateOf {
            allItems.filter { it.title.contains(query, true) }
        }
    }

    // remember с ключ: форматираните статистики се преизчисляват само при промяна на заявката
    val statsText = remember(query) {
        "Results for query \"$query\": ${filtered.value.size}"
    }

    Text(statsText)
    LazyColumn {
        items(filtered.value, key = { it.id }) { item ->
            Text(item.title)
        }
    }
}

filtered — е derivedStateOf, който автоматично се преизчислява при промяна на allItems или query. statsText използва remember(query) — скъпото форматиране на низ се изпълнява само при промяна на търсенето. Комбинацията от derivedStateOf и remember с ключове дава максимална производителност: производното състояние се изчислява само когато е необходимо, а сложните обекти се кешират до промяна на ключа.

Често задавани въпроси

Може ли remember да се използва извън Composable функция?

Не, remember — е функция от пакета compose.runtime, която изисква CompositionContext. Може да бъде извикана само вътре в @Composable функция или вътре в друга функция, извикана от Composable. За запазване на данни извън композицията използвайте ViewModel.

Какво се случва, ако не посоча ключ в remember?

Без ключ remember изчислява блока само веднъж — при първата композиция. Всички следващи рекомпозиции връщат запазената стойност. Ако трябва да преизчислявате стойността при промяна на данните, задължително ги посочете като ключ: remember(data) { compute(data) }.

Как да нулирам remember принудително?

Няма директно нулиране на remember. Единственият начин е да премахнете функцията от композицията (например, като я скриете чрез if условие) и след това да я покажете отново. При повторно влизане блокът на remember ще се изпълни отново, създавайки нова начална стойност.

По какво се различава remember от ViewModel?

remember съхранява състоянието в слота на Composable функцията и живее, докато функцията е в композицията. ViewModel живее, докато живее lifecycle-ът на екрана. ViewModel се запазва при завъртане и се използва за бизнес логика. remember — за локално UI състояние, което не е нужно извън една функция.

Работи ли remember в Preview компоненти?

Да, remember работи коректно в @Preview Composable функции, тъй като Preview създава пълноценен CompositionContext. Въпреки това, rememberSaveable може да не работи коректно в Preview, тъй като SavedStateHandle може да липсва в средата за преглед.

Резюме

  • remember запазва стойността в слота на композицията и оцелява при рекомпозиции на функцията
  • rememberSaveable допълнително запазва стойността при завъртане на екрана и спиране на процеса
  • derivedStateOf създава производно състояние с мързеливи изчисления
  • Ключове в remember(key) управляват преизчисляването на кешираната стойност
  • Нулиране на remember става само при премахване на функцията от композицията
  • Производителност: derivedStateOf и ключове предотвратяват ненужни изчисления при рекомпозиция
  • Препоръка: използвайте rememberSaveable за полета за въвеждане и remember + derivedStateOf за изчислени стойности

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също