Как обработать смену ориентации в 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 шт):

Автор решения: woesss

Всё просто: подписчик получает актуальные данные сразу при подписке, если они уже существуют и LifeCicleOwner находится в подходящем состоянии.
После поворота экрана LiveData уже содержит данные и подписчик их сразу получает, после вы дёргаете загрузку ещё раз и подписчик получает обновлённые (загруженные повторно) данные.
Если вам не нужно обновлять данные, если они однажды загружены, то загрузку нужно вызывать по условию, к примеру проверить наличие сохранения:

if (savedInstanceState == null) viewModel.getUserInfo()

Но поскольку в качестве ViewModelStoreOwner вы передаёте активити могут быть нюансы при смене фрагментов, тут надо знать логику приложения и желаемое поведение.

→ Ссылка