【问题标题】:How do popBackStack() and replace() operations differ?popBackStack() 和 replace() 操作有何不同?
【发布时间】:2013-07-21 12:32:58
【问题描述】:

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

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

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

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

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

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

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

【问题讨论】:

    标签: java android android-fragments


    【解决方案1】:

    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。因此,当您认为“用片段 B 替换,将片段 A 添加到后台堆栈”时,您实际上将整个操作添加到后台堆栈 - 即 用 B 替换 A。这个替换包括 2操作 - 删除 A 并添加 B。

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

    然后最后一步应该更清楚 - FragmentManager 不知道 B,所以当您在最后一步通过用 B 替换 A 来添加它时,B 需要通过其早期生命周期方法 - onAttach() 和 @ 987654330@.

    下面的代码说明了正在发生的事情。

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

    【讨论】:

    • 1.我已经编辑了我的问题,希望能进一步澄清。关于您的解决方案的奇怪之处在于,在这两种情况下,片段 B 似乎都进入了分离状态,然后又恢复了。 popBackStack() 一定在做不同的事情;我只是好奇那是什么。
    • 扩展了我的答案,希望这可以澄清事情。片段及其交易是相当棘手的主题。
    • 好的,所以如果我手动将 Fragment B 替换为 Fragment A,那么另一个交易记录将添加到后台堆栈中。这意味着 FragmentManager 仍然认为 Fragment B 处于分离状态。但是,当我弹出包含 Fragment B 的唯一事务时,FragmentManager 认为该 Fragment 不再附加,因为没有包含它的事务。这是正确的理解吗?另外,感谢您提供的超级有用的帮助。
    • 很高兴为您提供帮助!我会试着逐句回答。 (1) 我仅显示 2' 作为替代,效果与 2 相同 - 只是为了澄清。 (2) 我不确定你指的是哪个步骤,但是如果你添加了 B,并且没有从 FM 中完全删除它,它将保持连接和创建状态。当没有引用它的交易时,将完全删除。 (3) 基本上是的,只是 FM makes 片段不附加,即改变它的标志,调用 onDetach() 等等。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2012-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多