在 onActivityResult() 中提交 FragmentTransaction 是否安全?

Is it safe to commit a FragmentTransaction inside onActivityResult()?

几年前,当我试图在我的 onActivityResult() 回调中提交 FragmentTransaction 时,我 运行 在我的一个应用程序中遇到了问题。谷歌搜索,我发现 this question and answer,上面写着

At the time that onActivityResult() is called, the activity/fragment's state may not yet have been restored, and therefore any transactions that happen during this time will be lost as a result.

我最终为我的应用程序采用了同一答案中推荐的解决方案,生活很美好。然而,最近的实验表明情况可能发生了变化,现在 可能 onActivityResult().

内部提交 FragmentTransaction 是安全的

The documentation for (support v4) FragmentManager.beginTransaction() 将 t运行 操作的安全 window 定义为:

Note: A fragment transaction can only be created/committed prior to an activity saving its state. If you try to commit a transaction after FragmentActivity.onSaveInstanceState() (and prior to a following FragmentActivity.onStart or FragmentActivity.onResume(), you will get an error.

正在阅读the documentation for onActivityResult(),我明白了

You will receive this call immediately before onResume() when your activity is re-starting.

这让我相信在 onActivityResult() 中执行这些 t运行 操作应该是安全的,因为 onStart() 已经被调用,将我置于安全 window.

我制作了一个应用程序来对此进行测试,并且我成功地看到了我在 onActivityResult() 中创建和提交的对话框片段。我有同一个应用程序还记录了 activity 生命周期回调,因此我可以检查它们的顺序,我每次都看到 onStart(),然后是 onRestoreInstanceState(),然后是 onActivityResult()

我错过了什么吗?还是框架发生了变化,onActivityResult() 现在 gua运行 将成为片段 t运行 操作的安全场所?此行为是否因 API 级别而异?

我发现 似乎以与我相同的方式阅读文档,但两者都超过一年了,而且都没有具体提到 onActivityResult() 作为 t[=58 的安全位置=]sactions.

深入了解资源

FragmentManager class, called mStatedSaved. This variable keeps track of the saved state depending on the activity's lifecycle callbacks. Here's 方法中有一个布尔变量,它会抛出众所周知的异常:


    private void checkStateLoss() {
        if (mStateSaved) {
            throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
        }
        ...
    }

这意味着,一旦这个变量被更改为false,那么片段交易就可以自由执行了。当保存 activity 的状态时,此变量将变为 true,即 onSaveInstanceState().


回到问题

您说,您之前在从 onActivityResult() 提交事务时遇到问题。这应该意味着,以前 mStateSaved 没有被分配 false 而现在是。确实如此。

这是 O 版本的 onActivityResult() 实现:


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        mFragments.noteStateNotSaved();
        ...
    }

其中,noteStateNotSaved() 会执行以下操作:


    public void noteStateNotSaved() {
        ...
        mStateSaved = false;
        ...
    }

反之可以看到Jelly Bean发布onActivityResult()的实现:


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        int index = requestCode>>16;
        if (index != 0) {
            index--;
            if (mFragments.mActive == null || index = mFragments.mActive.size()) {
                Log.w(TAG, "Activity result fragment index out of range: 0x"
                        + Integer.toHexString(requestCode));
                return;
            }
            Fragment frag = mFragments.mActive.get(index);
            if (frag == null) {
                Log.w(TAG, "Activity result no fragment exists for index: 0x"
                        + Integer.toHexString(requestCode));
            } else {
                frag.onActivityResult(requestCode&0xffff, resultCode, data);
            }
            return;
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

没有任何东西会改变 mStateSaved 字段的值,这会在提交事务时抛出异常。

事实上,Dianne Hackborn 的 mFragments.noteStateNotSaved() was introduced in Kit-Kat release. As you can see by the commit's comment 行:

ActivityFragment should clear the flag that state is saved when it receives onNewIntent(). This can happen before the activity is resumed, so we may not have cleared it yet. Also need to do the same thing for onActivityResult().

总结

Is onActivityResult() now guaranteed to be a safe place for fragment transactions?

假设您使用的来源包括 commit 4ccc001,它是在 2012 年 10 月制作的 - 是的,它是片段交易的安全场所。