webentwicklung-frage-antwort-db.com.de

Mehrere LiveData Observer nach Fragmentierung

Problem

Zusammenfassung : Mehrere LiveDataObservers werden in einem Fragment ausgelöst, nachdem Sie zu einem neuen Fragment navigiert, das neue Fragment abgelegt und zu diesem zurückgekehrt sind das ursprüngliche Fragment.

Details : Die Architektur besteht aus MainActivity, das ein HomeFragment ​​als Startziel in hostet die Hauptaktivität Navigationsgrafik. In HomeFragment ist ein programmgesteuert aufgeblähtes PriceGraphFragment. Das HomeFragment ​​verwendet die Navigationskomponente, um ein neues untergeordnetes Fragment zu starten ProfileFragment. Beim Zurückdrücken wird das ProfileFragment eingeblendet und die App kehrt zum HomeFragment zurück, das das PriceGraphFragment hostet. Im PriceGraphFragment wird der Observer mehrmals aufgerufen.

Ich protokolliere den Hashcode der vom Observer ausgegebenen HashMap und es werden 2 eindeutige Hashcodes angezeigt, wenn ich zum Profilfragment gehe, das Profilfragment aufklappe und zum Preisfragment zurückkehre. Dies steht im Gegensatz zu dem Hashcode, der in der HashMap angezeigt wird, wenn ich den Bildschirm drehe, ohne das Profilfragment zu starten.

Implementierung

  1. Navigationskomponente zum Starten von ProfileFragment in HomeFragment.

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

  2. ViewModel Erstellung in Fragment (PriceGraphFragment). Das ViewModel wurde protokolliert und die Daten mit mehreren Beobachtern wurden nur einmal im ViewModel initialisiert.

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

  3. Suchen Sie nach Daten aus ViewModel im Originalfragment (PriceGraphFragment). Dies wird mehrmals aufgerufen, es wird jedoch nur ein Observer erwartet, wenn das Fragment geladen wird.

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

Versuchte Lösungen

  1. Erstellen der Fragment ​​'s ViewModel in der onCreate () Methode. priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.Java)
  2. Erstellen des ViewModel mithilfe der Aktivität des Fragments und des übergeordneten Fragments des untergeordneten Fragments.
    priceViewModel = ViewModelProviders.of(activity!!).get(PriceDataViewModel::class.Java)

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

  3. Verschieben von Methoden, mit denen die Observer erstellt werden, zu den Methoden onCreate () und onActivityCreated () des Fragments.
  4. Verwenden von viewLifecycleOwner anstelle von this für LifecycleOwner in der Methode observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer).
  5. Speichern der HashMap-Daten, die im ViewModel gegenüber dem Fragment als Duplikate angezeigt werden.
  6. Starten des untergeordneten Fragments mit dem ChildFragmentManager und dem SupportFragmentManager (auf der Aktivitätsebene).

Ähnliche Probleme und vorgeschlagene Lösungen

Nächste Schritte

  • Möglicherweise hängt das Problem mit der Erstellung des verschachtelten ChildFragment ​​( PriceGraphFragment ) im ParentFragment ​​( 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()
}
  • Testen Sie das Ersetzen der LiveData-Objekte durch RxJava-Observables.
10
Adam Hurwitz

Zunächst einmal vielen Dank an alle, die hier gepostet haben. Es war eine Kombination aus Ihren Ratschlägen und Hinweisen, die mir dabei geholfen haben, diesen Fehler in den letzten 5 Tagen zu beheben, da mehrere Probleme auftraten.

Probleme gelöst

  1. Verschachtelte Fragmente ordnungsgemäß im übergeordneten Fragment erstellen (HomeFragment).

Vor:

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()
    }
...
}

Nach dem:

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. Erstellen von ViewModel s in onCreate() im Gegensatz zu onCreateView() für übergeordnete und untergeordnete Fragmente. 

  2. Initialisierungsanforderung für Daten (Firebase Firestore-Abfrage) von untergeordneten Fragmenten (PriceFragment) in onCreate() und nicht in onViewCreated(), jedoch nur dann, wenn saveInstanceStatenull ist.

Nicht Faktoren

Es wurden einige Elemente vorgeschlagen, die sich jedoch bei der Behebung dieses Fehlers nicht auswirken.

  1. Beobachter in onActivityCreated() erstellen. Ich behalte meine in onViewCreated() des untergeordneten Fragments (PriceFragment).

  2. Verwendung von viewLifecycleOwner in der Observer Erstellung. Ich habe vorher die this des untergeordneten Fragments (PriceFragment) verwendet. Auch wenn sich viewLifecycleOwner nicht auf diesen Fehler auswirkt, scheint es die beste Vorgehensweise zu sein, also halte ich diese neue Implementierung.

1
Adam Hurwitz

Dies ist ein Grundfehler in der Architektur. Sie können mehr darüber lesen hier . Sie können das Problem lösen, indem Sie im Observer stattdessen getViewLifecycleOwner verwenden. So was:

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

Fügen Sie diesen Code in onActivityCreated () ein, da für die Verwendung von getViewLifecycleOwner eine Ansicht erforderlich ist.

1
KvdLingen