ViewModel не очищается в навигации, и данные в реальном времени в viewmodel остаются активными

Итак, я реализовал одно действие с шаблоном нескольких фрагментов с помощью навигации. Я использовал модель просмотра для каждого фрагмента для операций, отличных от пользовательского интерфейса.

Проблема в том, что когда вы перемещаетесь с помощью findNavController().navigate(), фрагмент фактически не уничтожается. Вызывается только onDestroyView. Таким образом, onDestroy фрагмента никогда не вызывается, и впоследствии модель просмотра не очищается, поэтому наблюдатель LiveData также остается живым, и когда я возвращаюсь к фрагменту, наблюдатель создается снова, и поэтому живые данные наблюдаются дважды. Один раз со старыми данными, которые он хранит, а второй - с новыми данными из некоторых операций.

Например, у меня есть фрагмент A и фрагмент B

A показывает список, а B вы можете добавить то, что будет отображаться в списке. возможно получение новых данных из API во фрагменте B для отображения в A.

Итак, когда я возвращаюсь от фрагмента B к A, наблюдатель вызывается дважды: сначала со старыми данными, а затем с обновленными данными. В конце концов, список показывает правильные данные, но я не хочу, чтобы присутствовали два наблюдателя.

Я следил за этой статьей https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808

и пытался использовать viewLifeCycleOwner вместо this, но это не помогло, и проблема все еще существует.

Я также попытался удалить наблюдателя перед наблюдением:

vm.ld.removeObservers(this)
vm.ld.observe(viewLifeCyclerOwner, observer)

по-прежнему проблема остается.

(Я также пробовал удалить наблюдателя в onDestroyView, но проблема осталась.)

Единственное, что я нашел, - это вручную вызвать модель просмотра onCleared в onDestroyView и очистить живые данные.

Во фрагменте onDestroyView

vm.clear()

В режиме просмотра

fun clear() = onCleared()

override fun onCleared() {
  //do stuff
}

Теперь это решает мою проблему. Но я считаю, что это не надежное решение, и может быть лучший способ сделать это. Буду рад, если кто-нибудь сможет пролить свет на это. Спасибо.


person Ron Daulagupu    schedule 17.12.2019    source источник
comment
братан у тебя есть решение? потому что у меня такая же проблема   -  person ken    schedule 24.04.2020
comment
вы можете попробовать решение, которое я использовал выше. Но учтите, что это не оптимальное решение.   -  person Ron Daulagupu    schedule 07.05.2020


Ответы (3)


Вы можете использовать viewModelStore.clear() в своем фрагменте, а затем переопределить onCleared() в ViewModel, чтобы избавиться от того, что вам нужно.

person kovac777    schedule 03.09.2020

Вы можете установить значение NULL для ваших данных в onDestroyView.

пример :

override fun onDestroyView() {
   super.onDestroyView()
   vm.ld.value = null
}

or

in ViewModel

fun resetLiveData() {
  _ld.value = null
}

in Fragment

override fun onDestroyView() {
   super.onDestroyView()
   vm.resetLiveData()
}
person Said Faisal    schedule 12.01.2021

Я потратил пару дней на устранение аналогичной проблемы. Дважды проверьте, как инициализируется ваша виртуальная машина:

val myVm: MyViewModel by activityViewModels()

vs.

val myVm: MyViewModel by viewModels()

Если вы используете делегат by activityViewModels(), вы инструктируете Android связать время жизни виртуальной машины с активностью хоста, а не с текущим фрагментом. Таким образом, ваша виртуальная машина не будет очищена, даже если фрагмент будет уничтожен. Я усвоил это на собственном горьком опыте. При обратном переключении на делегат by viewModels() виртуальная машина остается в пределах времени существования фрагмента. Когда фрагмент уничтожен, виртуальная машина очистится, если это единственный наблюдатель.

При наблюдении я был полностью сбит с толку целью this против viewLifecycleOwner. Очевидно, выбор цели имеет отношение только к тому, собираетесь ли вы вручную управлять представлением диалогового окна DialogFragment. Еще одна жемчужина замешательства.

В вашем случае, если вы переключаетесь между фрагментами, а onDestroy не вызывается, это также может быть связано с сохранением фрагментов. Например, ViewPager2 имеет offscreenPageLimit, который предписывает Android сохранять скрытые фрагменты в памяти при переключении страниц, что еще больше усугубляет этот беспорядок, заключающийся в необходимости знать абсолютно все обо всем только для того, чтобы использовать SDK.

person rmirabelle    schedule 13.07.2021