新的 FragmentTransaction commitNow() 在内部如何工作?

Posted

技术标签:

【中文标题】新的 FragmentTransaction commitNow() 在内部如何工作?【英文标题】:How is the new FragmentTransaction commitNow() working internally? 【发布时间】:2016-11-28 17:30:11 【问题描述】:

android N 和支持库版本 24 中添加的新 commitNow() 方法的文档有限且有点混乱。

同步提交此事务。任何添加的片段都将 初始化并完全进入其生命周期状态 主机和任何删除的碎片将在之前相应地被拆除 此调用返回。以这种方式提交事务允许 片段作为专用的封装组件添加 监控主机的生命周期状态,同时提供更牢固的 完全初始化这些片段时的排序保证 并准备好。管理视图的片段将创建这些视图 并附上。

调用 commitNow 优于调用 commit() 其次是 FragmentManager.executePendingTransactions() 作为后者 将产生尝试提交所有当前的副作用 待处理的交易,无论这是否是期望的行为。

以这种方式提交的事务可能不会被添加到 FragmentManager 的回栈,因为这样做会破坏其他预期 其他异步提交事务的排序保证。 如果事务处理,此方法将抛出 IllegalStateException 以前要求添加到后堆栈 addToBackStack(String).

一个事务只能用这个提交 方法在其包含活动之前保存其状态。如果 在此之后尝试提交,将引发异常。 这是因为如果活动,提交后的状态可能会丢失 需要从它的状态中恢复。请参阅 commitAllowingStateLoss() 了解 可能会丢失提交的情况。

我用粗体突出了我认为令人困惑的部分。

所以,我的主要顾虑/问题是:

1 - 不能添加它们?它说我会得到一个 IllegalStateException,那么它会被添加还是不会被添加?

2 - 我接受这样一个事实,即如果我们想在 backstack 中添加一个片段,我不能使用它。它没有说的是你得到了这个异常:

java.lang.IllegalStateException: This transaction is already being added to the back stack

!!!!????

所以我不能自己打电话给addToBackStack(String),因为它在内部为我打电话?我很抱歉,但是……什么?为什么?如果我不想将它添加到后台堆栈中怎么办?如果我稍后尝试使用 backstack 中的那个片段,但因为它可能不会被添加,后来它不存在怎么办?

如果我使用commitAllowingStateLoss(),这似乎是意料之中的事情,但我看到commitNowAllowingStateLoss() 也存在,所以......它遵循什么样的逻辑?

TL;DR

commitNow() 在内部是如何处理 backstack 的?

【问题讨论】:

现在5个月过去了,还是没人知道! @EpicPandaForce 已回答!希望我的回答能让你满意:) @NikoAdrianusYuwono 为了确保,我将获得更多的赏金,但这绝对是一个开始:p @EpicPandaForce 没问题!如果您仍有不清楚的地方,请告诉我!在我需要深入研究 Android 源代码的地方回答这样的问题总是很高兴;) 【参考方案1】:

当我们遇到这样的问题时,Android 源代码是开源的,这是一件好事!

回答

那么我们来看看BackStackRecord源码here

@Override
public void commitNow() 
    disallowAddToBackStack();
    mManager.execSingleAction(this, false);


@Override
public FragmentTransaction disallowAddToBackStack() 
    if (mAddToBackStack) 
        throw new IllegalStateException(
                "This transaction is already being added to the back stack");
    
    mAllowAddToBackStack = false;
    return this;

如果您在事务中调用addToBackStackmAddToBackStack 将设置为 true。

所以要回答您的问题,当您调用 commitNow() 时,不会在内部调用 addToBackStack,这是模棱两可的异常消息。我认为应该说 You're not allowed to add to backstack when using commitNow() 而不是当前消息。

奖金:

如果我们深入研究 FragmentManager 源代码 here,commitNow() 实际上与上面写的 executePendingTransactions()几乎相同的事情,但是执行所有先前提交的事务,commitNow() 将只提交该事务。

我认为这是 commitNow() 不允许添加到后台堆栈的主要原因,因为它不能保证没有任何其他未决事务。如果 commitNow() 可以添加到 backstack,我们有可能会破坏我们的 backstack 序列,这会导致意想不到的事情。

【讨论】:

是的,检查源代码它看起来是正确的答案。但不知何故,我觉得他们在谷歌 IO 中超卖了这个新 API ......因为他们试图在活动暂停后将它作为片段 IllegalStateException 问题的惊人解决方案出售,但不知何故这并没有提供完整的 commit() 功能,因为您无法将其添加到后台堆栈...因此,如果您的应用程序允许添加此片段而不将其添加到后台堆栈,则只能在某些情况下使用它...无论如何,正确答案!谢谢! @Kaizie 谢谢!是的,就像我上面写的一样,如果通过 commitNow() 添加将添加到 backstack,则有可能创建错误/意外的 backstack 顺序。让我们等待谷歌,也许他们有其他解决方案:D 或者如果我能想到更好的方法,会再次在这个答案中发表评论! 另见nikoyuwono.com/2016/12/13/fragment-transaction-commitnow。【参考方案2】:

长话短说,

如果您将addToBackStack()Fragment 一起使用,请不要 使用commitNow(),而是使用commit()

FragmentaddToBackStack()commitNow() 一起使用会导致片段事务排序不一致,因此必须使用commit()

详情请看article

【讨论】:

以上是关于新的 FragmentTransaction commitNow() 在内部如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

fragmentTransaction.add 和 fragmentTransaction.replace 之间的区别

操作栏在 FragmentTransaction replace() 和 FragmentTransaction add() 上的行为方式

Android FragmentTransaction.addToBackStack 混淆

FragmentTransaction.remove 没有效果

FragmentActivity 与使用 FragmentTransaction 管理 Fragment?

FragmentManager 和 FragmentTransaction 到底是做啥的?