Как сохранить состояние всех действий в навигации
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Сохранение состояния в Android Navigation Component
Сохранить состояние всех Activity в навигации можно через механизм ViewModel и SavedStateHandle, но для сохранения состояния самих фрагментов (которые являются основой современной навигации в Android) требуется особая настройка. Основная проблема заключается в том, что по умолчанию FragmentManager уничтожает неактивные фрагменты для освобождения памяти, теряя их состояние.
Ключевые подходы
1. Сохранение состояния через ViewModel + SavedStateHandle
Для сохранения простых данных (строки, примитивы) между конфигурационными изменениями и процессом смерти:
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
companion object {
private const val KEY_TEXT = "text_key"
}
val textLiveData = savedStateHandle.getLiveData<String>(KEY_TEXT)
fun saveText(text: String) {
savedStateHandle.set(KEY_TEXT, text)
}
}
2. Включение сохранения состояния фрагментов в Navigation Component
Для сохранения всех фрагментов в back stack необходимо настроить FragmentManager. Вот основной метод:
// В вашей Activity или родительском фрагменте
supportFragmentManager.enableSaveStateHandling(true)
// ИЛИ при настройке Navigation Component
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
// Сохраняем состояние фрагментов в back stack
navHostFragment.childFragmentManager.enableSaveStateHandling(true)
Но важно отметить: В последних версиях Navigation Component (2.4.0+) сохранение состояния включено по умолчанию для фрагментов в back stack. Проверьте вашу версию:
// build.gradle
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
3. Пользовательская реализация сохранения сложного состояния
Для сохранения сложных объектов (списков, кастомных классов):
class DetailFragment : Fragment() {
private lateinit var viewModel: DetailViewModel
private var complexData: MyComplexData? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Восстанавливаем состояние
savedInstanceState?.let {
complexData = it.getParcelable("complex_data")
}
viewModel = ViewModelProvider(this).get(DetailViewModel::class.java)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable("complex_data", complexData)
}
// Для сохранения при уничтожении процесса
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.state.collect { state ->
// Сохраняем в SavedStateHandle через ViewModel
}
}
}
}
}
Полное решение для сохранения состояния всей навигации
- Настройка NavHostFragment:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
// Важно: устанавливаем FragmentFactory для сохранения состояния
navHostFragment.childFragmentManager
.enableSaveStateHandling(true)
}
}
- Использование ViewModel со SavedStateHandle для каждого фрагмента:
class SharedViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
private val _navigationState = MutableStateFlow<NavigationState>(NavigationState.Default)
val navigationState: StateFlow<NavigationState> = _navigationState.asStateFlow()
fun saveNavigationState(state: NavigationState) {
savedStateHandle.set("nav_state", state)
}
init {
savedStateHandle.get<NavigationState>("nav_state")?.let {
_navigationState.value = it
}
}
}
- Глобальное сохранение состояния через OnBackPressedCallback:
class MainActivity : AppCompatActivity() {
private lateinit var appStateViewModel: AppStateViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appStateViewModel = ViewModelProvider(this).get(AppStateViewModel::class.java)
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// Сохраняем текущее состояние перед возвратом
saveCurrentFragmentsState()
if (!supportFragmentManager.popBackStackImmediate()) {
finish()
}
}
})
}
private fun saveCurrentFragmentsState() {
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment
navHostFragment?.let {
// Сохраняем состояние каждого фрагмента в back stack
it.childFragmentManager.fragments.forEach { fragment ->
(fragment as? StateSaver)?.saveState()
}
}
}
}
Рекомендации по архитектуре
- Используйте Single Activity Architecture с Navigation Component
- Храните навигационное состояние в SharedViewModel на уровне Activity
- Для критичных данных используйте персистентное хранилище (Room, DataStore)
- Тестируйте восстановление состояния через Developer Options → "Don't keep activities"
- Избегайте хранения тяжелых объектов в бандлах, используйте идентификаторы и восстанавливайте данные из базы
Важное предупреждение: Сохранение состояния всех фрагментов может потребовать значительной памяти. Оценивайте необходимость сохранения каждого фрагмента индивидуально и реализуйте очистку ненужных состояний в onLowMemory().