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

Какие знаешь способы сохранения данных экрана при пересоздании процесса приложения?

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

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

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

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

Сохранение состояния экрана при пересоздании процесса приложения

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

1. Сохранение состояния UI компонентов

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

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

// В Activity или Fragment
private val viewModel: UserViewModel by viewModels()

OnSaveInstanceState — механизм для сохранения небольших объемов данных (примитивы, Parcelable объекты) при временном уничтожении Activity. Восстанавливается в onCreate() или onRestoreInstanceState().

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putString("KEY_USER_NAME", userName)
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (savedInstanceState != null) {
        userName = savedInstanceState.getString("KEY_USER_NAME")
    }
}

Для Fragment используется setRetainInstance(true) (устаревший) или ViewModel с ViewModelProvider.

2. Сохранение бизнес-логики и более сложных данных

Repository Pattern в сочетании с корутинами/Flow — бизнес-логика должна храниться в репозиториях, которые независимы от жизненного цикла UI компонентов. Данные загружаются через корутины или Flow, которые могут пережить пересоздание.

class UserRepository {
    private val apiService: ApiService
    private val userDao: UserDao
    
    fun getUserFlow(userId: String): Flow<User> {
        return userDao.getUserFlow(userId)
            .onEach { user ->
                if (user == null) {
                    // Загрузка из сети
                    val networkUser = apiService.getUser(userId)
                    userDao.insert(networkUser)
                }
            }
    }
}

Использование статических объектов или Dependency Injection — через Dagger/Hilt или Koin можно обеспечить существование ключевых компонентов вне жизненного цикла Activity/Fragment.

3. Постоянное хранение данных

Когда приложение полностью уничтожается системой (процесс убит), вышеуказанные механизмы не работают. Здесь нужны решения для постоянного хранения:

Room Database — рекомендуемая ORM для SQLite, обеспечивающая реактивное программирование через Flow/LiveData.

@Entity
data class User(
    @PrimaryKey val id: String,
    val name: String,
    val email: String
)

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE id = :userId")
    fun getUserFlow(userId: String): Flow<User?>
}

SharedPreferences — для хранения простых ключ-значение данных. Современная реализация через DataStore (предпочтительнее).

DataStore — замена SharedPreferences с поддержкой корутин и Flow, типобезопасность.

val Context.userPreferencesDataStore: DataStore<Preferences> by preferencesDataStore(
    name = "user_prefs"
)

suspend fun saveUserToken(token: String) {
    context.userPreferencesDataStore.edit { preferences ->
        preferences[stringPreferencesKey("user_token")] = token
    }
}

Файловая система — для больших данных, изображений, документов.

Сетевое хранилище — синхронизация с бэкендом для восстановления данных после переустановки приложения.

Стратегия комбинирования подходов

На практике я комбинирую несколько методов:

  1. Мелкие UI состоянияonSaveInstanceState (текст в поле ввода, позиция скролла)
  2. Данные для текущего экранаViewModel + LiveData/StateFlow
  3. Кэшированные данныеRoom с Flow, чтобы при пересоздании UI автоматически получал актуальные данные
  4. Настройки пользователяDataStore
  5. Критичные данные → синхронизация с бэкендом

Пример восстановления после убийства процесса:

class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Восстановление из Bundle (мелкие данные)
        val tempName = savedInstanceState?.getString("TEMP_NAME")
        
        // Подписка на Flow из Room (основные данные)
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.userFlow.collect { user ->
                    // Обновление UI
                }
            }
        }
        
        // Если данных нет нигде - загрузка из сети
        if (viewModel.userFlow.value == null && tempName == null) {
            viewModel.loadUserFromNetwork()
        }
    }
}

Ключевой принцип: чем важнее данные, тем более устойчивое хранилище нужно использовать. Для UI состояния хватит ViewModel, для пользовательских данных — база данных, для критичной информации — синхронизация с сервером. Современные архитектурные подходы (MVVM, MVI) с четким разделением ответственности значительно упрощают управление состоянием при пересоздании процесса.