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

В каком классе хранить состояния State

2.0 Middle🔥 201 комментариев
#Архитектура и паттерны#Жизненный цикл и навигация

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

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

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

Общие принципы хранения State в Android

Вопрос о классе для хранения состояния (State) в Android приложениях зависит от нескольких ключевых факторов: архитектуры приложения, типа состояния (UI состояние vs бизнес–логика), жизненного цикла компонентов, и требований к тестируемости и поддерживаемости. Рассмотрим основные подходы и их применение.

Типы состояния и места хранения

1. UI состояние (состояние экрана/фрагмента/активности)

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

Рекомендуемые подходы:

  • ViewModel (архитектурный компонент Android Jetpack)основной выбор для UI состояния в современных приложениях:

    class ProductListViewModel : ViewModel() {
        private val _products = MutableStateFlow<List<Product>>(emptyList())
        val products: StateFlow<List<Product>> = _products.asStateFlow()
        
        private val _loading = MutableStateFlow(false)
        val loading: StateFlow<Boolean> = _loading.asStateFlow()
        
        fun loadProducts() {
            _loading.value = true
            // Загрузка данных
            _products.value = fetchedProducts
            _loading.value = false
        }
    }
    

    Преимущества:

    • Переживает смену конфигурации
    • Отделяет UI логику от жизненного цикла Activity/Fragment
    • Интегрируется с Jetpack Compose через collectAsState() или с View–системой через LiveData
  • SavedStateHandle (в составе ViewModel) – для сохранения состояния при убийстве процесса:

    class DetailsViewModel(
        private val savedStateHandle: SavedStateHandle
    ) : ViewModel() {
        companion object {
            private const val SELECTED_ID_KEY = "selected_id"
        }
        
        var selectedId: Int
            get() = savedStateHandle[SELECTED_ID_KEY] ?: 0
            set(value) { savedStateHandle[SELECTED_ID_KEY] = value }
    }
    

2. Состояние навигации

  • NavController (Jetpack Navigation) – хранит состояние стека навигации
  • Аргументы фрагментов (Bundle) – для передачи данных между экранами

3. Состояние бизнес–логики/доменной логики

Это состояние, не зависящее от UI, часто связано с данными приложения.

Рекомендуемые подходы:

  • Repository / Data Layer – централизованное хранение данных:

    class UserRepository(
        private val localDataSource: LocalDataSource,
        private val remoteDataSource: RemoteDataSource
    ) {
        private val _currentUser = MutableStateFlow<User?>(null)
        val currentUser: StateFlow<User?> = _currentUser.asStateFlow()
        
        suspend fun fetchUser(userId: String) {
            val user = remoteDataSource.getUser(userId)
            localDataSource.saveUser(user)
            _currentUser.value = user
        }
    }
    
  • Use Cases / Interactors – для инкапсуляции конкретных бизнес–правил

4. Состояние всего приложения (App–Scope State)

Данные, доступные во всем приложении (например, аутентификация, тема).

Рекомендуемые подходы:

  • Singleton–классы (осторожно, могут затруднять тестирование)
  • DI–контейнер с соответствующим scope (например, @Singleton в Dagger/Hilt):
    @Singleton
    class AppStateManager @Inject constructor() {
        private val _darkModeEnabled = MutableStateFlow(false)
        val darkModeEnabled: StateFlow<Boolean> = _darkModeEnabled.asStateFlow()
        
        fun toggleTheme() {
            _darkModeEnabled.value = !_darkModeEnabled.value
        }
    }
    

Критерии выбора класса для хранения State

При принятии решения учитывайте:

  1. Жизненный цикл:

    • ViewModel – привязан к жизненному циклу UI компонента
    • Repository – обычно существует дольше (пока живет Dependency Graph)
    • Activity/Fragment – только для временного состояния, не переживающего смену конфигурации
  2. Тестируемость:

    • ViewModel и Repository легко тестировать с моками зависимостей
    • Избегайте хранения сложного состояния в Activity/Fragment
  3. Объем состояния:

    • Малые данные – можно в SavedStateHandle
    • Крупные данные (списки, сложные объекты) – в Repository/DataSource с кэшированием
  4. Архитектурный паттерн:

    • MVVM – основное состояние в ViewModel
    • MVI – состояние как иммутабельный объект в ViewModel
    • Clean Architecture – состояние распределено по слоям (Domain, Data, Presentation)

Анти–паттерны (чего избегать)

  • Хранение критического состояния в статических переменных – утечки памяти, проблемы с тестированием
  • Прямое хранение в Activity/Fragment поля без механизмов сохранения – данные потеряются при повороте
  • Глобальные переменные – нарушение инкапсуляции, сложность отладки

Современные тенденции (2023–2024)

  1. StateFlow / SharedFlow вместо LiveData для реактивного состояния
  2. Корутины для асинхронных операций с состоянием
  3. Jetpack Compose с mutableStateOf() для UI состояния внутри @Composable функций:
    @Composable
    fun CounterScreen() {
        var count by remember { mutableStateOf(0) }
        
        Column {
            Text(text = "Count: $count")
            Button(onClick = { count++ }) {
                Text("Increment")
            }
        }
    }
    
  4. Unidirectional Data Flow (UDF) – однонаправленный поток данных для предсказуемости изменений состояния

Рекомендации по выбору

Для типичного современного Android приложения:

  • UI состояние экранаViewModel с StateFlow/LiveData
  • Состояние навигацииNavController + аргументы
  • Данные приложенияRepository с кэшированием
  • Состояние процессаSavedStateHandle (в ViewModel)
  • Глобальные настройкиDataStore/SharedPreferences + Singleton/DI

Правильное разделение состояния по соответствующим классам согласно принципу единственной ответственности (SOLID) значительно улучшает поддерживаемость, тестируемость и надежность приложения.