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

Как реализовывается сохранение состояния объекта в современных проектах?

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

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

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

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

Сохранение состояния в современных Android-проектах

В современных Android-приложениях сохранение состояния объекта реализуется через комбинацию архитектурных подходов, специализированных компонентов и библиотек Jetpack. Ключевой парадигмой стало разделение ответственности между UI-слоем и слоем данных.

Основные подходы и инструменты

1. ViewModel с SavedStateHandle

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

class UserViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    
    companion object {
        private const val SELECTED_USER_ID_KEY = "selected_user_id"
    }
    
    var selectedUserId: Int
        get() = savedStateHandle[SELECTED_USER_ID_KEY] ?: 0
        set(value) = savedStateHandle.set(SELECTED_USER_ID_KEY, value)
    
    // Состояние через StateFlow
    private val _uiState = MutableStateFlow(UserUiState())
    val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
}

2. UI State с использованием StateFlow/SharedFlow

Современный подход предполагает моделирование состояния как неизменяемых data-классов и использование реактивных потоков:

data class UserUiState(
    val isLoading: Boolean = false,
    val users: List<User> = emptyList(),
    val errorMessage: String? = null
)

class UserRepository {
    private val _usersState = MutableStateFlow(UsersState())
    val usersState: StateFlow<UsersState> = _usersState.asStateFlow()
    
    suspend fun loadUsers() {
        _usersState.update { it.copy(isLoading = true) }
        try {
            val users = apiService.getUsers()
            _usersState.update { it.copy(isLoading = false, users = users) }
        } catch (e: Exception) {
            _usersState.update { it.copy(isLoading = false, errorMessage = e.message) }
        }
    }
}

3. Сохранение в постоянное хранилище

Для данных, которые должны сохраняться между сессиями приложения:

  • DataStore (предпочтительный современный способ):
val Context.settingsDataStore: DataStore<Preferences> by preferencesDataStore(
    name = "settings"
)

class SettingsRepository(private val dataStore: DataStore<Preferences>) {
    val themePreference = dataStore.data.map { preferences ->
        preferences[THEME_KEY] ?: Theme.SYSTEM_DEFAULT
    }
    
    suspend fun saveTheme(theme: Theme) {
        dataStore.edit { preferences ->
            preferences[THEME_KEY] = theme
        }
    }
    
    companion object {
        val THEME_KEY = preferencesKey<Theme>("theme")
    }
}
  • Room для структурированных данных:
@Entity
data class User(
    @PrimaryKey val id: Int,
    val name: String,
    val lastSeen: Long
)

@Dao
interface UserDao {
    @Upsert
    suspend fun saveUser(user: User)
    
    @Query("SELECT * FROM user WHERE id = :userId")
    fun getUser(userId: Int): Flow<User?>
}

4. Восстановление состояния в Compose

В Jetpack Compose состояние управляется через remember и rememberSaveable:

@Composable
fun UserProfileScreen(
    viewModel: UserViewModel = hiltViewModel()
) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    
    // Сохранение состояния в композиции
    var expanded by rememberSaveable { mutableStateOf(false) }
    
    // Восстановление сложных объектов через Saver
    val userSelectionState = rememberSaveable(
        saver = UserSelection.Saver
    ) { mutableStateOf(UserSelection()) }
}

Ключевые принципы современного подхода

Разделение ответственности

  • ViewModel хранит UI-состояние, переживающее смену конфигурации
  • Repository управляет бизнес-логикой и данными
  • DataStore/Room обеспечивают постоянное хранение
  • Composable управляет локальным UI-состоянием

Реактивность и односторонний поток данных

// Паттерн MVI (Model-View-Intent)
sealed interface UserIntent {
    object LoadUsers : UserIntent
    data class SelectUser(val id: Int) : UserIntent
}

class UserViewModel : ViewModel() {
    private val _intent = MutableSharedFlow<UserIntent>()
    
    init {
        viewModelScope.launch {
            _intent.collect { intent ->
                when (intent) {
                    is UserIntent.LoadUsers -> loadUsers()
                    is UserIntent.SelectUser -> selectUser(intent.id)
                }
            }
        }
    }
    
    fun processIntent(intent: UserIntent) {
        viewModelScope.launch {
            _intent.emit(intent)
        }
    }
}

Безопасность и производительность

  • Использование Coroutines и Flow для асинхронных операций
  • Кэширование данных в памяти при помощи ViewModel
  • Сериализация только необходимых данных через Parcelable/Saver
  • Шифрование чувствительных данных в DataStore

Рекомендации по реализации

  1. Определите жизненный цикл данных: временные (в памяти), сессионные (SavedStateHandle), постоянные (база данных)
  2. Используйте dependency injection (Hilt/Dagger) для управления зависимостями
  3. Реализуйте обработку ошибок как часть состояния UI
  4. Пишите unit-тесты для ViewModel и UseCases
  5. Документируйте ожидаемое поведение при смерти процесса

Современный подход делает акцент на реактивности, тестируемости и безопасности, уходя от устаревших методов вроде прямого использования onSaveInstanceState() в Activity/Fragment к декларативным и управляемым паттернам.

Как реализовывается сохранение состояния объекта в современных проектах? | PrepBro