在提交FragmentTransaction之前检查状态是否已保存

时间:2013-02-13 18:16:20

标签: android android-fragments fragmentmanager android-savedstate onsaveinstancestate

我有时会看到以下用于提交的堆栈跟踪,当用户未查看活动时(状态已保存后)可能会发生提交:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1327)
    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1338)
    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

查看Android源码,这很有意义:

private void checkStateLoss() {
        if (mStateSaved) {
            throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
        }
        if (mNoTransactionsBecause != null) {
            throw new IllegalStateException(
                    "Can not perform this action inside of " + mNoTransactionsBecause);
        }
 }

现在,我想知道是否有任何方法(除了在(保存/恢复)InstanceState中存储类变量)以检查片段是否将在不合需要的状态下提交,这样我可以存储事务稍后在适当的时候进行提交。

8 个答案:

答案 0 :(得分:38)

从支持库版本26.0.0 Beta 1开始,FragmentManagerFragment类中提供了一个新API:

  

FragmentManager Fragment 有一个 isStateSaved()方法,可以查询是否允许在没有状态丢失的情况下进行交易。这在检查执行任何事务之前处理 onClick()事件时特别有用。

来自android.support.v4.app.FragmentManager#isStateSaved()的文档:

  

如果 FragmentManager 的状态已由其主机保存,则返回 true 。如果此方法返回 true ,则不应执行任何会更改已保存状态的操作。例如,任何 popBackStack()方法,例如 popBackStackImmediate()或任何 FragmentTransaction ,使用 commit()代替 commitAllowingStateLoss()会改变状态并导致错误。

此API将从Android O开始使用框架android.app.FragmentManager

答案 1 :(得分:20)

由于您没有附加任何示例代码,我只能猜测您在提交事务时使用的是“错误”方法。

所以,您应该使用,而不是使用FragmentTransaction.commit() FragmentTransaction.commitAllowingStateLoss()

此外,this google blog post中有关于此问题(或更改API行为)的报告和解决方法。

答案 2 :(得分:13)

不幸的是,如果提交事务没有问题,Android Fragment不会提供API检查。

但是我们可以在附加的活动中添加一个布尔字段来帮助我们检查。请参考以下代码。

public class GlobalBaseActivity extends FragmentActivity {

    private boolean mAllowCommit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mAllowCommit = true;
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        mAllowCommit = false;
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onResumeFragments() {
        mAllowCommit = true;
        super.onResumeFragments();
    }

    public boolean allowFragmentCommit() {
        return mAllowCommit;
    } 

    public void callbackOnEvent() {
        if (allowFragmentCommit()){
            getFragmentManager().beginTransaction().add(new YourFragment(), TAG).commit();
        }
    }
}

至于为什么选择onResumeFragment()作为允许交易指标,请参考this good blog。它详细解释了着名的IllegalStateException。

答案 3 :(得分:0)

RuntimeExceptions(和IllegalStateException就是其中之一)通常意味着你的代码在尝试实现某些东西时是不正确的。试图处理这种异常(例如通过捕获它)也是错误的。你的代码应该永远不会像Android那样抛出这样的异常。

如果您使用处理程序并发布或发送将来要处理的消息,则需要在退出恢复状态之前清除队列。 此外,您不能只在Activity中启动AsyncTask并在onPostExecute中提交事务,因为用户可以从您的Activity返回。您需要取消它并检查它是否被取消。

有很多像这样的例子,都是由“临时”内存泄漏引起的,就像我喜欢称它们一样。

基本上你的代码很糟糕而且没有提供它,就不可能知道如何。

答案 4 :(得分:0)

在这种情况下,

ft.commitAllowingStateLose()是你最好的选择。但是,如上所述,无法保证您的州将继续存在。

答案 5 :(得分:0)

您应该仅在这些方法OnCreate,OnResumeFragments,OnPostResume中调用方法提交。您可以在此处阅读详细信息Fragment Transactions & Activity State Loss

答案 6 :(得分:-1)

看到我也有这个问题,我找不到任何解决方案。我试过一个解决方法。它的工作方式就像这样。而是从Handler进行片段事务。应该使用2000毫秒的DELAY调用处理程序。 例如。在调用makeTransaction

void makeTransaction()
{
  handler.sendEmptyMessageDelayed(0,2000);
}

void actuallyMakeTransaction()
{
 // transaction code
}

Handler handler = new Handler()
{
void handleMessage(Message msg)
{
   actuallyMakeTransaction();
}
}

它解决了我的问题

答案 7 :(得分:-1)

这是一个利用最新的isStateSaved()FragmentManager方法的Kotlin示例:https://proandroiddev.com/kotlin-extensions-to-commit-fragments-safely-de06218a1f4