← Назад к вопросам

Какие знаешь способы хранения текущего состояния экрана?

2.0 Middle🔥 181 комментариев
#Жизненный цикл и навигация#Работа с данными

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы хранения текущего состояния экрана в Android

В Android существует несколько ключевых подходов к сохранению состояния экрана, каждый из которых решает разные сценарии: от временного хранения данных при повороте устройства до постоянного сохранения пользовательского прогресса. Вот основные методы:

1. ViewModel

ViewModel — это компонент архитектуры Android, предназначенный для хранения и управления UI-данными жизненно-независимым способом. Он переживает изменения конфигурации (например, поворот экрана), но уничтожается при окончательном завершении активити/фрагмента.

class UserViewModel : ViewModel() {
    private val _userName = MutableLiveData<String>()
    val userName: LiveData<String> = _userName
    
    fun updateName(name: String) {
        _userName.value = name
    }
}

// Использование в Activity/Fragment
class MyActivity : AppCompatActivity() {
    private lateinit var viewModel: UserViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        
        viewModel.userName.observe(this) { name ->
            // Обновление UI
        }
    }
}

2. Сохранение состояния через onSaveInstanceState()

Этот механизм позволяет сохранить небольшой объем данных (сериализуемых в Bundle) при временном уничтожении активити (из-за нехватки памяти или изменения конфигурации). Данные восстанавливаются в onCreate() или onRestoreInstanceState().

class MainActivity : AppCompatActivity() {
    private var currentScore: Int = 0
    
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putInt("SCORE_KEY", currentScore)
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState != null) {
            currentScore = savedInstanceState.getInt("SCORE_KEY", 0)
        }
    }
}

3. Сохранение состояния в Fragment (setRetainInstance)

Устаревший способ, где фрагмент не пересоздавался при изменении конфигурации. Вместо него теперь рекомендуется использовать ViewModel вместе с SavedStateHandle.

4. SavedStateHandle в ViewModel

Расширение ViewModel, позволяющее сохранять состояние даже при полном уничтожении процесса приложения (системой для освобождения памяти).

class SavedStateViewModel(
    private val state: SavedStateHandle
) : ViewModel() {
    companion object {
        private const val SEARCH_QUERY_KEY = "query"
    }
    
    val query: LiveData<String> = state.getLiveData(SEARCH_QUERY_KEY)
    
    fun setQuery(query: String) {
        state.set(SEARCH_QUERY_KEY, query)
    }
}

5. Персистентное хранение

Для долговременного сохранения состояния между сессиями приложения используются:

  • SharedPreferences — для простых ключ-значение данных
  • Базы данных (Room) — для структурированных данных
  • DataStore — современная замена SharedPreferences с поддержкой Kotlin Coroutines/Flow
  • Файловая система — для сложных объектов или больших данных
// Пример с DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

class SettingsRepository(private val dataStore: DataStore<Preferences>) {
    val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
        .map { preferences ->
            UserPreferences(
                darkMode = preferences[PreferencesKeys.DARK_MODE] ?: false
            )
        }
    
    suspend fun updateDarkMode(enabled: Boolean) {
        dataStore.edit { preferences ->
            preferences[PreferencesKeys.DARK_MODE] = enabled
        }
    }
}

6. Архитектурные подходы

  • MVI (Model-View-Intent) — состояние экрана описывается единым иммутабельным объектом
  • StateFlow/SharedFlow в сочетании с ViewModel для реактивного управления состоянием
class NewsViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()
    
    data class NewsUiState(
        val isLoading: Boolean = false,
        val articles: List<Article> = emptyList(),
        val error: String? = null
    )
}

Критерии выбора подхода

  1. Временное состояние при поворотеViewModel
  2. Восстановление после убийства процессаViewModel + SavedStateHandle или onSaveInstanceState()
  3. Долговременное хранение между запускамиDataStore, Room, SharedPreferences
  4. Комплексное UI-состояниеStateFlow в ViewModel или MVI
  5. Навигационные аргументыSafe Args или сохранение в ViewModel

Практические рекомендации

  • Избегайте хранения больших объектов (битмапы, списки) в Bundle — используйте идентификаторы и перезагружайте данные
  • Разделяйте ответственность: UI-состояние — во ViewModel, постоянные данные — в репозиториях
  • Тестируйте сценарии с убийством процесса (можно включить опцию "Don't keep activities" в настройках разработчика)
  • Для навигации между фрагментами передавайте минимально необходимые идентификаторы, а не сложные объекты

Современный рекомендуемый стек: ViewModel + SavedStateHandle для управления состоянием жизненного цикла, StateFlow для реактивного UI, DataStore для простых постоянных данных и Room для структурированного хранения. onSaveInstanceState() остается полезным для краевых случаев, когда ViewModel еще не инициализирована в ранних этапах жизненного цикла.

Какие знаешь способы хранения текущего состояния экрана? | PrepBro