26

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

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

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

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

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

请注意,Fragment A 和 Fragment B 的所有实例都是在其宿主 Activity 启动时创建的。

编辑澄清。第二次调用的情况onCreate()如下:附加Fragment A => 用Fragment B 替换,将Fragment A 添加到后栈=> 使用Fragment A 弹出Fragment A popBackStack()=> 再次用Fragment B 替换Fragment A。

4

2 回答 2

92

replace()做两件事:

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

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

现在popBackStack()反转您添加到 BackStack 的最后一个事务。

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

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

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

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

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

然后,下一步是弹出包含此替换的事务。所以你没有弹出 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();
于 2013-07-22T17:42:14.393 回答
0

这可能是因为片段管理器重用片段实例而不是重新创建它。如果 Fragment B 中的代码onCreate()需要在显示 Fragment 时执行,则将代码移动到另一种方法中,例如onResume().

于 2013-07-22T17:28:29.867 回答