popBackStack() 和 replace() 操作有何不同?

2022-09-01 16:21:34

在管理 Fragments 时,我在应用程序中遇到了一些奇怪的行为,我想知道 SO 是否可以帮助阐明为什么会发生这种情况。

我有两个片段,我们称它们为片段A和片段B。我的应用程序的一般流程是,当用户以某种方式与片段 A 交互时,片段 B 通过调用显示(在所有情况下都会发生这种情况)。当我显示片段B时,我将片段A添加到后堆栈;然后,当用户按下片段 B 上的后退按钮时,片段 A 会通过从后退堆栈弹出来再次显示。fragmentTransaction.replace()

这一切都很好,但是今天我发现有一个来自片段B的流调用,用当前在后堆栈上的片段A的相同实例替换片段B。fragmentTransaction.replace()

这本身并没有错,但是当我从片段A回到片段B时,会出现奇怪的行为。如果我调用 ,则不调用片段 B 的方法。fragmentTransaction.replace()onCreate()

但是,如果我从后退堆栈中弹出 Fragment A,然后将其替换为 Fragment B,则会触发 Fragment B 的方法。这是为什么呢?onCreate()

请注意,片段 A 和片段 B 的所有实例都是在其主机活动启动时创建的。

编辑以进行澄清。第二次调用的情况如下:附加片段 A = >替换为片段 B,将片段 A 添加到后退堆栈 = >使用 = >再次将片段 A 替换为片段 B。onCreate()popBackStack()


答案 1

replace()做两件事:

  1. 从您指示的容器 (C) 中删除当前添加的片段 (A)
  2. 将新片段 (B) 添加到同一容器

这2个操作是保存为Backstack记录/事务的操作。请注意,片段 A 仍处于状态,其视图被销毁。created

现在,反转您添加到BackStack的最后一笔交易。popBackStack()

在这种情况下,这将是2个步骤:

  1. 从 C 中删除 B
  2. 将 A 添加到 C

在此之后,片段 B 变为 ,如果您不保留对它的引用,它将被垃圾回收。detached

要回答问题的第一部分,没有呼叫,因为FragmentB仍处于状态。对问题第二部分的回答有点长。onCreate()created

首先,重要的是要了解您实际上并没有添加到Backstack,而是添加了.因此,当您认为您“用片段 B 替换,将 Fragment A 添加到后退堆栈”时,您实际上将整个操作添加到 backstack 中 - 即用 B 替换 A。此替换包括 2 个操作 - 删除 A 和添加 B。FragmentsFragmentTransactions

然后,下一步是弹出包含此替换的事务。所以你不是在弹出FragmentA,而是在反转“删除A,添加B”,反转的是“删除B,添加A”。

然后最后一步应该更清晰 - FragmentManager没有意识到B,所以当你在最后一步通过用B替换A来添加它时,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()


推荐