44

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

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

现在,我想知道是否有任何方法(除了将类变量存储在 on(Save/Restore)InstanceState 中)来检查片段是否会以不希望的状态提交,这样我可以存储事务以供以后使用在适当的时间提交。

4

8 回答 8

48

从支持库版本26.0.0 Beta 1FragmentManager开始,在类中提供了一个新的 API Fragment

FragmentManagerFragment有一个isStateSaved()方法来允许查询是否允许在不丢失状态的情况下进行事务。这对于在执行任何事务之前处理onClick()事件时进行检查特别有用。

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

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

此 API 将与android.app.FragmentManager从 Android O 开始的框架一起提供。

于 2017-05-19T07:37:13.383 回答
20

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

因此,FragmentTransaction.commit()您应该使用 FragmentTransaction.commitAllowingStateLoss()而不是使用。

此外,在这个 google blog post中有关于这个问题(或者更确切地说是 API 行为的变化)的报告和解决方法。

于 2013-03-21T19:17:11.623 回答
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()允许交易指标,请参考这个好博客。它详细解释了著名的 IllegalStateException。

于 2015-05-18T08:03:45.820 回答
0

RuntimeExceptions(IllegalStateException 就是其中之一)通常意味着您的代码在尝试实现某事的方式上是不正确的。试图处理这种异常(例如通过捕获它)也是错误的。你的代码不应该表现得像 Android 会向你抛出这样的异常。

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

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

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

于 2013-03-21T21:23:08.153 回答
0

ft.commitAllowingStateLose()在这种情况下是你最好的选择。但是,如前所述,不能保证您的状态会持续存在。

于 2014-06-02T18:29:16.823 回答
0

您应该只在这些方法 OnCreate、OnResumeFragments、OnPostResume 中调用方法 commit。您可以在此处阅读的详细信息片段交易和活动状态损失

于 2015-08-13T12:27:58.180 回答
0

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

于 2017-11-09T13:42:32.817 回答
-1

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

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

void actuallyMakeTransaction()
{
 // transaction code
}

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

它为我解决了问题

于 2013-03-18T06:04:11.830 回答