web-dev-qa-db-fra.com

En quoi les opérations popBackStack () et replace () diffèrent-elles?

J'ai rencontré un comportement curieux dans mon application lors de la gestion des fragments et je me demandais si SO pourrait aider à éclairer pourquoi cela se produit.

J'ai deux fragments, nous les appellerons Fragment A et Fragment B. Le flux général de mon application est que lorsque l'utilisateur interagit avec le Fragment A d'une manière ou d'une autre, le Fragment B est affiché en appelant fragmentTransaction.replace() (this arrive dans tous les cas). Lorsque je montre le fragment B, j'ajoute le fragment A à la pile arrière; puis, lorsque l'utilisateur appuie sur le bouton de retour du fragment B, le fragment A s'affiche à nouveau en sortant de la pile arrière.

C'est bien beau, mais aujourd'hui, j'ai découvert qu'il y a un flux provenant du fragment B qui appelle fragmentTransaction.replace(), remplaçant le fragment B par la même instance de fragment A qui est actuellement sur la pile arrière.

En soi, il n'y a rien de mal à cela, mais le comportement étrange survient lorsque je retourne du fragment A au fragment B. Si j'appelle fragmentTransaction.replace(), la méthode onCreate() du fragment B est pas appelé.

Cependant, si j'ai extrait le fragment A de la pile arrière et que je l'ai remplacé par le fragment B, la méthode onCreate() du fragment B est déclenchée. Pourquoi est-ce?

Notez que toutes les instances du fragment A et du fragment B sont créées au moment du lancement de leur activité hôte.

Modifier pour clarification. Le cas où onCreate() est appelé une deuxième fois est le suivant: Attachez le fragment A => remplacez par le fragment B, en ajoutant le fragment A à la pile arrière => éclatez le fragment A en utilisant popBackStack() = > remplacez à nouveau le fragment A par le fragment B.

23
Jonathan

replace() fait 2 choses:

  1. Retirez le fragment (A) actuellement ajouté du conteneur (C) que vous avez indiqué
  2. Ajouter un nouveau fragment (B) dans le même conteneur

Ces 2 opérations sont ce qui est sauvegardé en tant qu'enregistrement/transaction Backstack. Notez que le fragment A reste dans l'état created et sa vue est détruite.

Maintenant, popBackStack() annule votre dernière transaction que vous avez ajoutée à BackStack.

Dans ce cas, ce serait 2 étapes:

  1. Supprimer B de C
  2. Ajouter A à C

Après cela, le fragment B devient detached, et si vous n'en avez pas conservé les références, il sera récupéré.

Pour répondre à la première partie de votre question, il n'y a pas d'appel onCreate(), car FragmentB est resté dans l'état created. Et la réponse à la deuxième partie de la question est un peu plus longue.

Tout d'abord, il est important de comprendre que vous n'ajoutez pas réellement Fragments à Backstack, vous ajoutez FragmentTransactions. Donc, lorsque vous pensez que vous "remplacez par le fragment B, en ajoutant le fragment A à la pile arrière", vous ajoutez en fait toute cette opération au backstack - c'est-à-dire remplacement de A par B. Ce remplacement se compose de 2 actions - supprimer A et ajouter B.

Ensuite, l'étape suivante consiste à extraire la transaction qui contient ce remplacement. Donc, vous ne faites pas éclater FragmentA, vous inversez "supprimer A, ajouter B", qui inversé est "supprimer B, ajouter A".

Et la dernière étape devrait être plus claire - il n'y a pas de B que FragmentManager connaisse, donc lorsque vous l'ajoutez en remplaçant A par B à votre dernière étape, B doit suivre ses premières méthodes de cycle de vie - onAttach() et onCreate().

Le code ci-dessous illustre ce qui se passe.

FragmentManager fm  = getFragmentManager();
FragmentA fragmentA = new FragmentA();
FragmentB fragmentB = new FragmentB();

// 1. Show A
fm.beginTransaction()
  .add(fragmentA, R.id.container)
  .commit();

// 2. Replace A with B
// FragmentManager keeps reference to fragmentA;
// it stays attached and created; fragmentB goes 
// through lifecycle methods onAttach(), onCreate()
// and so on.
fm.beginTransaction()
  .replace(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();

// 2'. Alternative to replace() method
fm.beginTransaction()
  .remove(fragmentA)
  .add(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();

// 3. Reverse (2); Result - A is visible
// What happens:
//   1) fragmentB is removed from container, it is detached now;
//      FragmentManager doesn't keep reference to it anymore
//   2) Instance of FragmentA is placed back in the container
// Now your Backstack is empty, FragmentManager is aware only
// of FragmentA instance
fm.popBackStack();

// 4. Show B
// Since fragmentB was detached, it goes through its early
// lifecycle methods: onAttach() and onCreate().
fm.beginTransaction()
  .replace(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();
81
mindeh

C'est peut-être parce que le gestionnaire de fragments réutilise l'instance de fragment plutôt que de la recréer. Si le code à l'intérieur du fragment B onCreate() doit être exécuté lorsque le fragment est affiché, déplacez le code dans une autre méthode comme onResume().

0
tbruyelle