Подтвердить что ты не робот

Чем отличаются операции popBackStack() и replace()?

Я столкнулся с каким-то любопытным поведением в моем приложении при управлении Фрагментами, и мне было интересно, сможет ли SO помочь пролить свет на то, почему это происходит.

У меня есть два фрагмента, мы будем называть их фрагментом A и фрагментом B. Общий поток моего приложения заключается в том, что когда пользователь каким-то образом взаимодействует с фрагментом A, фрагмент B отображается, вызывая fragmentTransaction.replace() (это происходит в любом случае). Когда я показываю фрагмент B, я добавляю фрагмент A в задний стек; затем, когда пользователь нажимает кнопку "Назад" на фрагменте B, фрагмент A снова отображается, выбирая из заднего стека.

Это все хорошо и хорошо, но сегодня я обнаружил, что есть поток из фрагмента B, который вызывает fragmentTransaction.replace(), заменяя фрагмент B тем же экземпляром фрагмента A, который в настоящее время находится в фоновом стеке.

В самом деле, в этом нет ничего плохого, однако странное поведение возникает, когда я возвращаюсь из фрагмента А в фрагмент B. Если я назвал fragmentTransaction.replace(), метод onCreate() фрагмента B не вызывается.

Однако, если я вытащил фрагмент A из заднего стека, а затем заменил его на фрагмент B, будет запущен метод onCreate() фрагмента B. Почему это?

Обратите внимание, что все экземпляры фрагмента A и фрагмента B создаются во время запуска его активности на хосте.

Изменить для уточнения. Случай, когда onCreate() вызывается во второй раз, заключается в следующем: Прикрепить фрагмент A = > заменить фрагментом B, добавив фрагмент A в задний стек = > поп-фрагмент A, используя popBackStack() = > снова заменить фрагмент A фрагментом B.

4b9b3361

Ответ 1

replace() делает 2 вещи:

  • Удалить добавленный фрагмент (A) из контейнера (C), который вы указали
  • Добавить новый фрагмент (B) в тот же контейнер

Эти 2 операции - это то, что сохраняется как запись/транзакция Backstack. Обратите внимание, что фрагмент A остается в состоянии created, и его представление уничтожено.

Теперь popBackStack() отменяет вашу последнюю транзакцию, добавленную в BackStack.

В этом случае это будет 2 шага:

  • Удалить B из C
  • Добавить A в C

После этого фрагмент B становится detached, и если вы не сохранили ссылки на него, это будет собирать мусор.

Чтобы ответить на первую часть вашего вопроса, нет вызова onCreate(), потому что FragmentB остался в состоянии created. И ответ на вторую часть вопроса немного длиннее.

Во-первых, важно понимать, что вы фактически не добавляете Fragments в Backstack, вы добавляете FragmentTransactions. Поэтому, когда вы думаете, что вы "замените фрагментом B, добавив фрагмент A в задний стек", вы фактически добавите всю эту операцию в стопку - это замена A на B. Эта замена состоит из двух действий - удалите A и добавьте B.

Затем следующий шаг - это посылка транзакции, которая содержит эту замену. Таким образом, вы не выходите из FragmentA, вы меняете "удалить A, добавьте B", который обратный - "удалить B, добавить A".

И тогда последний шаг должен быть более ясным - нет B, о котором знает FragmentManager, поэтому, добавив его, заменив A на B на последнем шаге, B должен пройти его ранние методы жизненного цикла - onAttach() и onCreate().

Нижеприведенный код иллюстрирует, что происходит.

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();

Ответ 2

Возможно, это связано с тем, что диспетчер фрагментов повторно использует экземпляр фрагмента, а не воссоздает его. Если код внутри фрагмента B onCreate() требуется выполнить, когда отображается фрагмент, затем переместите код другим способом, например onResume().