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

Как сохранить данные из ViewModel для переиспользования

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

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

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

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

Сохранение данных из ViewModel для переиспользования

В архитектуре Android приложений ViewModel играет ключевую роль в хранении и управлении UI-mданными, связанными с жизненным циклом активити или фрагмента. Для переиспользования данных между различными экранами, сессиями приложения или даже после завершения процесса необходимо использовать комбинацию подходов.

Основные стратегии сохранения данных

1. Сохранение в локальной базе данных

Наиболее надежный способ для структурных данных, которые должны сохраняться между сеансами приложения.

class ProductViewModel(
    private val productRepository: ProductRepository
) : ViewModel() {
    private val _products = MutableLiveData<List<Product>>()
    val products: LiveData<List<Product>> = _products
    
    fun loadProducts() {
        viewModelScope.launch {
            // Загрузка из базы данных
            val cachedProducts = productRepository.getProductsFromDb()
            _products.value = cachedProducts
            
            // Обновление из сети с сохранением в БД
            val freshProducts = productRepository.fetchProductsFromApi()
            productRepository.saveProductsToDb(freshProducts)
            _products.value = freshProducts
        }
    }
}

2. Использование SharedPreferences/DataStore

Для простых ключ-значение данных, таких как настройки пользователя, токены авторизации или флаги.

class SettingsViewModel(
    private val dataStore: DataStore<Preferences>
) : ViewModel() {
    val darkModeEnabled = dataStore.data
        .map { preferences ->
            preferences[PreferencesKeys.DARK_MODE] ?: false
        }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
    
    fun toggleDarkMode(enabled: Boolean) {
        viewModelScope.launch {
            dataStore.edit { preferences ->
                preferences[PreferencesKeys.DARK_MODE] = enabled
            }
        }
    }
}

object PreferencesKeys {
    val DARK_MODE = booleanPreferencesKey("dark_mode_enabled")
}

3. Кэширование в памяти с Singleton-репозиторием

Для временного хранения данных, которые должны быть доступны разным ViewModel в пределах одного сеанса приложения.

object SessionCache {
    private val userCache = ConcurrentHashMap<String, User>()
    private val productCache = mutableListOf<Product>()
    
    fun cacheUser(user: User) {
        userCache[user.id] = user
    }
    
    fun getCachedUser(userId: String): User? {
        return userCache[userId]
    }
}

class UserViewModel : ViewModel() {
    fun loadUser(userId: String): LiveData<User> {
        return liveData {
            // Проверка кэша в памяти
            val cachedUser = SessionCache.getCachedUser(userId)
            if (cachedUser != null) {
                emit(cachedUser)
                return@liveData
            }
            
            // Загрузка из других источников
            val user = userRepository.loadUser(userId)
            SessionCache.cacheUser(user)
            emit(user)
        }
    }
}

Архитектурные рекомендации

  • Разделение ответственности: ViewModel не должна напрямую работать с хранилищами. Используйте Repository pattern для абстрагирования источника данных.
  • Единый источник истины (SSOT): Определите главный источник данных для каждого типа информации.
  • Реактивное программирование: Используйте LiveData, StateFlow или SharedFlow для наблюдения за изменениями данных.

Практический пример с полной архитектурой

// Repository с многоуровневым кэшированием
class UserRepository(
    private val userDao: UserDao,
    private val apiService: ApiService,
    private val cache: MemoryCache
) {
    suspend fun getUser(userId: String): User {
        // 1. Проверка памяти
        cache.getUser(userId)?.let { return it }
        
        //132. Проверка локальной БД
        val localUser = userDao.getUserById(userId)
        if (localUser != null) {
            cache.saveUser(localUser)
            return localUser
        }
        
        // 3. Загрузка из сети
        val remoteUser = apiService.getUser(userId)
        userDao.insertUser(remoteUser) // Сохранение в БД
        cache.saveUser(remoteUser) // Сохранение в памяти
        
        return remoteUser
    }
}

// ViewModel, использующая репозиторий
class UserProfileViewModel(
    private val userRepository: UserRepository
) : ViewModel() {
    private val _userState = MutableStateFlow<UserState>(UserState.Loading)
    val userState: StateFlow<UserState> = _userState
    
    fun loadUserProfile(userId: String) {
        viewModelScope.launch {
            _userState.value = UserState.Loading
            try {
                val user = userRepository.getUser(userId)
                _userState.value = UserState.Success(user)
            } catch (e: Exception) {
                _userState.value = UserState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

Ключевые принципы

  • Соответствие данных жизненному циклу: Определите, как долго должны храниться данные
  • Синхронизация между источниками: Реализуйте стратегию обновления кэшей
  • Обработка ошибок: Предусмотрите fallback-механизмы при недоступности данных
  • Тестируемость: Используйте dependency injection для замены источников данных в тестах

Выбор стратегии зависит от типа данных, требований к персистентности и архитектуры приложения. Для большинства современных Android-приложений рекомендуется комбинированный подход: Room для структурных данных, DataStore для настроек и in-memory кэш для временных данных сессии.

Как сохранить данные из ViewModel для переиспользования | PrepBro