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

Как можно сохранить данные при пересоздании Activity?

2.3 Middle🔥 221 комментариев
#Android компоненты#Жизненный цикл и навигация

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

Сохранение данных при пересоздании Activity

Activity пересоздаётся при поворотах экрана, смене языка системы, нехватке памяти и других причинах. Потеря данных при этом — плохой UX. Существует несколько способов сохранить состояние. Каждый подходит для разных сценариев.

1. ViewModel + LiveData/StateFlow (рекомендуется)

Это самый правильный способ в современном Android.

class MyViewModel : ViewModel() {
    private val _userLiveData = MutableLiveData<User>()
    val userLiveData: LiveData<User> = _userLiveData
    
    private val _counterState = MutableStateFlow<Int>(0)
    val counterState: StateFlow<Int> = _counterState.asStateFlow()
    
    fun loadUser(id: Int) {
        viewModelScope.launch {
            _userLiveData.value = repository.getUser(id)
        }
    }
    
    fun incrementCounter() {
        _counterState.value += 1
    }
}

class MyActivity : AppCompatActivity() {
    private val viewModel: MyViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Данные сохраняются автоматически при пересоздании Activity
        viewModel.userLiveData.observe(this) { user ->
            displayUser(user)
        }
        
        // Состояние не потеряется при повороте
        lifecycleScope.launch {
            viewModel.counterState.collect { counter ->
                counterText.text = counter.toString()
            }
        }
    }
}

Плюсы:

  • Автоматически выживает при повороте и других пересозданиях
  • Lifecycle-aware, нет утечек памяти
  • Асинхронность из коробки
  • Современный способ

Минусы:

  • Требует AndroidX

2. savedInstanceState (onSaveInstanceState)

Для простых данных (примитивы, Parcelable, Serializable).

class MyActivity : AppCompatActivity() {
    companion object {
        private const val KEY_USER = "user_key"
        private const val KEY_COUNTER = "counter_key"
    }
    
    private var user: User? = null
    private var counter = 0
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Восстановить из Bundle
        if (savedInstanceState != null) {
            user = savedInstanceState.getParcelable(KEY_USER)
            counter = savedInstanceState.getInt(KEY_COUNTER, 0)
        } else {
            loadData()  // Новое создание
        }
    }
    
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // Сохранить данные перед пересозданием
        outState.putParcelable(KEY_USER, user)
        outState.putInt(KEY_COUNTER, counter)
    }
}

Плюсы:

  • Встроено в Android Framework
  • Подходит для простых данных

Минусы:

  • Bundle ограничена по размеру (~1MB)
  • Не подходит для больших объектов
  • Много boilerplate-кода
  • Надо вручную сохранять/восстанавливать

3. SharedPreferences

Для простых, долгоживущих данных (настройки, токены).

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
        
        // Восстановить
        val savedCounter = prefs.getInt("counter", 0)
        val savedUser = prefs.getString("user_name", null)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        
        // Сохранить
        val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
        prefs.edit().apply {
            putInt("counter", counter)
            putString("user_name", user?.name)
            apply()
        }
    }
}

// Лучше использовать EncryptedSharedPreferences для конфиденциальных данных
val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

val encryptedSharedPreferences = EncryptedSharedPreferences.create(
    context,
    "secret_shared_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

Плюсы:

  • Простой API
  • Персистентное хранилище
  • Подходит для глобальных настроек

Минусы:

  • Не для сложных структур
  • Медленнее чем оперативная память
  • Без типизации (нужны обёртки)

4. Room (Database)

Для больших объёмов данных, которые нужны долгоживущие.

@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey val id: Int,
    val name: String,
    val email: String
)

@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getUserById(id: Int): UserEntity?
    
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUser(user: UserEntity)
}

class MyViewModel(
    private val userDao: UserDao
) : ViewModel() {
    fun loadUser(id: Int) {
        viewModelScope.launch {
            val user = userDao.getUserById(id)
            _userLiveData.value = user
        }
    }
}

Плюсы:

  • Типизированное хранилище
  • Мощные запросы
  • Транзакции, индексы
  • Автоматические миграции

Минусы:

  • Overhead для простых случаев
  • Требует dependencies

5. Файловая система (Files, Cache)

Для больших файлов (изображения, видео).

class MyActivity : AppCompatActivity() {
    private fun saveData(data: String) {
        val cacheDir = cacheDir  // Очищается при удалении приложения
        val file = File(cacheDir, "my_data.txt")
        file.writeText(data)
    }
    
    private fun loadData(): String? {
        val cacheDir = cacheDir
        val file = File(cacheDir, "my_data.txt")
        return if (file.exists()) file.readText() else null
    }
}

Плюсы:

  • Неограниченный размер
  • Подходит для больших файлов

Минусы:

  • Нет типизации
  • Медленнее чем память
  • Нужно обрабатывать I/O

Сравнительная таблица

СпособРазмерСкоростьКогда использоватьСложность
ViewModelUnlimitedБыстроТекущее состояние экранаНизкая
savedInstanceState~1MBОчень быстроПростые примитивыСредняя
SharedPreferencesUnlimitedБыстроНастройки, токеныНизкая
RoomUnlimitedБыстроКешированные данные, историяСредняя
FilesUnlimitedМедленноБольшие объёмы (фото, видео)Средняя

Лучшие практики

// ✅ Комбинированный подход (рекомендуется)
class MyViewModel : ViewModel() {
    // Текущее состояние экрана
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    
    // Кешированные данные в базе
    private val userRepository: UserRepository
    
    // При создании, восстанови из кеша
    fun loadUser(id: Int) {
        viewModelScope.launch {
            // Сначала показать из кеша
            val cached = userRepository.getUserFromCache(id)
            _uiState.value = UiState.Loaded(cached)
            
            // Затем загрузить свежие данные
            val fresh = userRepository.getUserFromNetwork(id)
            _uiState.value = UiState.Loaded(fresh)
        }
    }
}

Современный stack для сохранения данных:

  1. ViewModel — текущее состояние UI
  2. Room — кеш и история
  3. SharedPreferences/EncryptedSharedPreferences — конфиденциальные настройки
  4. savedInstanceState — только если нужны примитивы

Эта комбинация обеспечивает надёжное восстановление при пересоздании Activity и сохранение данных между сеансами приложения.

Как можно сохранить данные при пересоздании Activity? | PrepBro