web-dev-qa-db-fra.com

Plusieurs observateurs LiveData après fragment de saut

Problème

Résumé: Plusieurs LiveDataObserverssont déclenchés dans un fragment après avoir navigué vers un nouveau fragment, extrait le nouveau fragment et est retourné au fragment d'origine.

Détails: L'architecture est composée de MainActivityqui héberge un HomeFragmenten tant que destination de départ} dans le _ de MainActivity graphique de navigation. Dans HomeFragment est un programme gonflé PriceGraphFragment. Le HomeFragmentutilise le composant de navigation pour lancer un nouveau fragment enfant ProfileFragment. Au retour, le ProfileFragment est affiché et l'application revient au HomeFragment hébergeant le PriceGraphFragment. Le PriceGraphFragment est l'endroit où l'observateur est appelé plusieurs fois.

Je suis en train de consigner le hashcode de la table de hachage émise par Observer. Deux codes de hachage uniques sont alors affichés lorsque je vais au fragment de profil, saute le fragment de profil et retourne au fragment de prix. Ceci est opposé à l'un hashcode vu de la HashMap quand je tourne l'écran sans lancer le fragment de profil.

La mise en oeuvre

  1. Composant de navigation pour lancer le nouveau ProfileFragment dans HomeFragment.

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

  2. ViewModelcreation in Fragment (PriceGraphFragment). Le ViewModel a été enregistré et les données associées à plusieurs observateurs possèdent des données initialisées une seule fois dans ViewModel.

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

  3. Écoutez les données de ViewModeldans le fragment d'origine (PriceGraphFragment). Cet appel est appelé plusieurs fois. Toutefois, il ne devrait y avoir qu'un observateur lorsque le fragment est chargé.

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

Tentative de solutions

  1. Création du Fragment's ViewModeldans le onCreate ()method). priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.Java)
  2. Création du ViewModel à l'aide de l'activité du fragment et du fragment parent du fragment enfant.
    priceViewModel = ViewModelProviders.of(activity!!).get(PriceDataViewModel::class.Java)

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

  3. Déplacement des méthodes qui créent les observateurs vers les méthodes onCreate ()et onActivityCreated ()) du fragment.
  4. Utilisation de viewLifecycleOwner au lieu de this pour LifecycleOwnerdans la méthode observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer).
  5. Stockage des données HashMap affichées en tant que doublons dans le ViewModel opposé au fragment.
  6. Lancer le fragment enfant en utilisant les variables ChildFragmentManager et SupportFragmentManagerau niveau de l'activité).

Problèmes similaires et solutions proposées

Prochaines étapes

  • Le problème est peut-être lié à la création du _ (ChildFragment(PriceGraphFragment) imbriqué dans ParentFragments (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()
}
  • Testez le remplacement des objets LiveData par des observables RxJava.
5
Adam Hurwitz

Tout d’abord, merci à tous ceux qui ont posté ici. C’est une combinaison de vos conseils et de vos conseils qui m’a aidé à résoudre ce problème au cours des 5 derniers jours, car de nombreux problèmes étaient en cause.

Problèmes résolus

  1. Création correcte de fragments imbriqués dans le fragment parent (HomeFragment).

Avant:

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

Après:

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. Création de ViewModel s dans onCreate() par opposition à onCreateView() pour les fragments parent et enfant. 

  2. Initialisation de la demande de données (requête Firebase Firestore) du fragment enfant (PriceFragment) dans onCreate() plutôt que onViewCreated(), mais ne le faisant que lorsque saveInstanceState est null .

Non facteurs

Quelques éléments ont été suggérés mais n’ont pas eu d’impact sur la résolution de ce bug.

  1. Création de Observers in onActivityCreated(). Je garde le mien dans onViewCreated() du fragment enfant (PriceFragment).

  2. Utiliser viewLifecycleOwner dans Observer creation. J'utilisais la variable this de l'enfant Fragment (PriceFragment) auparavant. Même si viewLifecycleOwner n’a pas d’impact sur ce bogue, il semble que ce soit une bonne pratique, donc je conserve cette nouvelle implémentation.

1
Adam Hurwitz

C’est un bug dans l’architecture. Vous pouvez en lire plus à ce sujet ici . Vous pouvez le résoudre en utilisant getViewLifecycleOwner au lieu de cela dans l'observateur. Comme ça:

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

Et mettez ce code dans onActivityCreated (), car l'utilisation de getViewLifecycleOwner nécessite une vue.

1
KvdLingen