Multiple LiveData Observers After Popping Fragment

旧城冷巷雨未停 提交于 2019-12-12 10:35:38

问题


Issue

Summary: Multiple LiveData Observers are being triggered in a Fragment after navigating to a new Fragment, popping the new Fragment, and returning to the original Fragment.

Details: The architecture consists of MainActivity that hosts a HomeFragment as the start destination in the MainActivity's navigation graph. Within HomeFragment is a programmatically inflated PriceGraphFragment. The HomeFragment is using the navigation component to launch a new child Fragment ProfileFragment. On back press the ProfileFragment is popped and the app returns to the HomeFragment hosting the PriceGraphFragment. The PriceGraphFragment is where the Observer is being called multiple times.

I'm logging the hashcode of the HashMap being emitted by the Observer and it is showing 2 unique hashcodes when I go to the profile Fragment, pop the profile Fragment, and return to the price Fragment. This is opposed to the one hashcode seen from the HashMap when I rotate the screen without launching the profile Fragment.

Implementation

  1. Navigation component to launch new ProfileFragment within HomeFragment.

    view.setOnClickListener(Navigation.createNavigateOnClickListener( R.id.action_homeFragment_to_profileFragment, null))

  2. ViewModel creation in Fragment (PriceGraphFragment). The ViewModel has been logged and the data that has multiple Observers only has data initialized in the ViewModel once.

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.java) }

  3. Listen for data from ViewModel in original Fragment (PriceGraphFragment). This is being called multiple times, however it is only expected to have one Observer when the Fragment is loaded.

    priceViewModel.graphLiveData.observe( this, Observer { priceGraphDataMap: HashMap<Exchange, PriceGraphLiveData>? -> // This is being called multiple times. })

Attempted Solutions

  1. Creating the Fragment's ViewModel in the onCreate() method. priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.java)
  2. Creating the ViewModel using the Fragment's activity and the child Fragment's parent Fragment.
    priceViewModel = ViewModelProviders.of(activity!!).get(PriceDataViewModel::class.java)

    priceViewModel = ViewModelProviders.of(parentFragment!!).get(PriceDataViewModel::class.java)

  3. Moving methods that create the Observers to the Fragment's onCreate() and onActivityCreated() methods.
  4. Using viewLifecycleOwner instead of this for the LifecycleOwner in the method observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer).
  5. Storing the HashMap data that is showing as duplicates in the ViewModel opposed to the Fragment.
  6. Launching the child Fragment using the ChildFragmentManager and the SupportFragmentManager (on the Activity level).

Similar Issues and Proposed Solutions

  • https://github.com/googlesamples/android-architecture-components/issues/47
  • https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808
  • https://plus.google.com/109072532559844610756/posts/Mn9SpcA5cHz

Next Steps

  • Perhaps the issue is related to creating the nested ChildFragment (PriceGraphFragment) in the ParentFragment's (HomeFragment) onViewCreated()?

ParentFragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    user = viewModel.getCurrentUser()
     if (savedInstanceState == null) {
         fragmentManager
                ?.beginTransaction()
                ?.replace(binding.priceDataContainer.id, 
                   PriceGraphFragment.newInstance())
                ?.commit()
}
  • Test replacing the LiveData objects with RxJava observables.

回答1:


This is basically a bug in the architecture. You can read more about it here. You can solve it by using getViewLifecycleOwner instead of this in the observer.

Like this:

mViewModel.methodToObserve().observe(getViewLifecycleOwner(), new Observer<Type>() {
        @Override
        public void onChanged(@Nullable Type variable) {

And put this code in onActivityCreated() as the use of getViewLifecycleOwner requires a view.




回答2:


First off, thank you to everyone who posted here. It was a combination of your advice and pointers that helped me solve this bug over the past 5 days as there were multiple issues involved.

Issues Resolved

  1. Creating nested Fragments properly in parent Fragment (HomeFragment).

Before:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {

        if (savedInstanceState == null) {
        fragmentManager
                ?.beginTransaction()
                ?.add(binding.priceDataContainer.id, PriceGraphFragment.newInstance())
                ?.commit()
        fragmentManager
                ?.beginTransaction()
                ?.add(binding.contentFeedContainer.id, ContentFeedFragment.newInstance())
                ?.commit()
    }
...
}

After:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

    if (savedInstanceState == null
            && childFragmentManager.findFragmentByTag(PRICEGRAPH_FRAGMENT_TAG) == null
            && childFragmentManager.findFragmentByTag(CONTENTFEED_FRAGMENT_TAG) == null) {
        childFragmentManager.beginTransaction()
                .replace(priceDataContainer.id, PriceGraphFragment.newInstance(),
                        PRICEGRAPH_FRAGMENT_TAG)
                .commit()
        childFragmentManager.beginTransaction()
                .replace(contentFeedContainer.id, ContentFeedFragment.newInstance(),
                        CONTENTFEED_FRAGMENT_TAG)
                .commit()
    }
...
}
  1. Creating ViewModels in onCreate() as opposed to onCreateView() for both the parent and child Fragments.

  2. Initializing request for data (Firebase Firestore query) data of child Fragment (PriceFragment) in onCreate() rather than onViewCreated() but still doing so only when saveInstanceState is null.

Non Factors

A couple items were suggested but turned out to not have an impact in solving this bug.

  1. Creating Observers in onActivityCreated(). I'm keeping mine in onViewCreated() of the child Fragment (PriceFragment).

  2. Using viewLifecycleOwner in the Observer creation. I was using the child Fragment (PriceFragment)'s this before. Even though viewLifecycleOwner does not impact this bug it seems to be best practice overall so I'm keeping this new implementation.



来源:https://stackoverflow.com/questions/51892478/multiple-livedata-observers-after-popping-fragment

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!