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

How is the new FragmentTransaction commitNow() working internally?

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

Commits this transaction synchronously. Any added fragments will be initialized and brought completely to the lifecycle state of their host and any removed fragments will be torn down accordingly before this call returns. Committing a transaction in this way allows fragments to be added as dedicated, encapsulated components that monitor the lifecycle state of their host while providing firmer ordering guarantees around when those fragments are fully initialized and ready. Fragments that manage views will have those views created and attached.

Calling commitNow is preferable to calling commit() followed by FragmentManager.executePendingTransactions() as the latter will have the side effect of attempting to commit all currently pending transactions whether that is the desired behavior or not.

Transactions committed in this way may not be added to the FragmentManager's back stack, as doing so would break other expected ordering guarantees for other asynchronously committed transactions. This method will throw IllegalStateException if the transaction previously requested to be added to the back stack with addToBackStack(String).

A transaction can only be committed with this method prior to its containing activity saving its state. If the commit is attempted after that point, an exception will be thrown. This is because the state after the commit can be lost if the activity needs to be restored from its state. See commitAllowingStateLoss() for situations where it may be okay to lose the commit.

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

所以,我的主要 concerns/questions 是:

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

2 - 我接受这样一个事实,即如果我们想在后台堆栈中添加一个片段,我将无法使用它。它没有说的是你得到这个例外:

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

!!!!????

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

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

TL;DR

commitNow() 在内部如何处理后台堆栈?

当我们遇到这样的问题时,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 将被设置为真。

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

奖金:

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

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

长话短说,

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

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

详细说明看这个article