Как обработать смену ориентации в MVVM правильно?
Есть активность и несколько фрагментов. У каждого фрагмента есть доступ к ViewModel. Проблема состоит в том что после поворота устройства и возврата на фрагмент срабатывает observable два раза. Вот ViewModel:
class AppViewModel(private val mainRepository: MainRepository) : ViewModel() {
//поле которое обновляется
val userInfo: MutableLiveData<Any> by lazy {
MutableLiveData<Any>()
}
...
//метод который обновляет поле
fun getUserInfo() {
...
}
}
работа с ViewModel в фрагменте:
class PersonalPage : Fragment(R.layout.fragment_personal_page) {
lateinit var viewModel: AppViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewBinding = FragmentPersonalPageBinding.inflate(inflater, container, false)
viewBinding!!.lifecycleOwner = viewLifecycleOwner
val retrofitService = RetrofitService.getInstance(requireContext())
val mainRepository = MainRepository(retrofitService)
viewModel = ViewModelProvider(
requireActivity(),
AppVMFactory(mainRepository)
)[AppViewModel::class.java]
return viewBinding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.userInfo.observe(viewLifecycleOwner) {
it?.let {
if (it is UserInfo) {
println("viewmodel fetch")
...
}
}
}
viewModel.getUserInfo()
}
....
}
обработка поворота экрана в активности для возврата на фрагмент:
supportFragmentManager.findFragmentByTag("personal_page")?.let {
transaction.replace(R.id.contentContainer, it,"personal_page").commit()
} ?: run {
transaction.replace(R.id.contentContainer, personalPage,"personal_page").commit()
}
пробовал убирать слушатель при повороте:
viewModel.userInfo.removeObservers(viewLifecycleOwner)
так не сработало. Пробовал убивать viewmodel вообще присваивая null - не сработало. При том что я под отладкой посмотрел и у меня один экземпляр фрагмента в стеке, то есть дублирования нет. Можно ли как-то удалять слушатель для переменной после поворота экрана или вообще есть другой способ обработать смену ориентации? Как вариант можно удалять фрагмент после поворота, и переходить на него по новой, без поиска фрагмента по тэгу, но мне кажется это неправильным.
Ответы (1 шт):
Всё просто: подписчик получает актуальные данные сразу при подписке, если они уже существуют и LifeCicleOwner находится в подходящем состоянии.
После поворота экрана LiveData уже содержит данные и подписчик их сразу получает, после вы дёргаете загрузку ещё раз и подписчик получает обновлённые (загруженные повторно) данные.
Если вам не нужно обновлять данные, если они однажды загружены, то загрузку нужно вызывать по условию, к примеру проверить наличие сохранения:
if (savedInstanceState == null) viewModel.getUserInfo()
Но поскольку в качестве ViewModelStoreOwner вы передаёте активити могут быть нюансы при смене фрагментов, тут надо знать логику приложения и желаемое поведение.