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

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中存储类变量之外,是否有任何方法可以检查片段是否将被提交到不良状态,这样我就可以稍后存储事务并在适当的时间进行提交。


2
你是否正在尝试从 onNewIntent 方法中执行 FragmentTransaction - Vladimir Mironov
可能吗?那有什么区别呢? - hwrdprkns
1
我今天遇到了同样的问题,原因是当活动还没有恢复时,我尝试从onNewIntent添加一个新片段。 - Vladimir Mironov
8个回答

48

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

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

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

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

此API将随Android O一起发布到框架的android.app.FragmentManager中。


3
看起来非常有帮助。 - i.shadrin

21

3
当调用popBackStack时怎么办?没有popBackStackAllowingStateLoss。 - Greg Ennis
7
不应该调用FragmentTransaction.commitAllowingStateLoss(),这只能是最后的选择。请注意。 - Rushi Ayyappa

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。


1
我之前就有这个想法(是的,在提问之前我确实读了那篇博客)。这是正确的方法。commitAllowingStateLoss()并不是解决问题的好方法。 - Sufian
不幸的是,Android Fragment 没有提供 API 来检查提交事务是否正常。幸运的是,他们知道如何做,这在 回答中提到了。 - azizbekian

0

RuntimeExceptions(其中IllegalStateException是其中之一)通常意味着您的代码在尝试实现某些操作时不正确。尝试处理此类异常(例如通过捕获它)也是错误的。您的代码不应该像Android会向您抛出这样的异常那样行为。

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

有许多类似这样的例子,所有这些都是由“临时”内存泄漏引起的,我喜欢称之为。

基本上,您的代码很糟糕,没有提供它,无法确定如何解决。


7
这并没有回答问题,只是批评了它。 - Eurig Jones
1
@Amorgos 这是真的。这样的问题需要一些批评。这样的问题根源于不尝试理解一个设计良好的平台,就像医生试图治疗症状而不是原因一样。如果代码存在,原因将很容易找到。 - MaciejGórski
3
请将该问题标记为对应类别。发帖人的意图是提出一个技术问题,并期望得到技术性回答。他没有展示足够的证据来支持一个技术性回答,你是正确的。但是,你的回答与问题存在相同的问题!如果你对一个问题有疑问,那么请标记它或在其下方评论。 - Eurig Jones

0

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


0

0

-1

我也遇到了这个问题,但是无论在哪里都找不到解决方案。我尝试了一个变通方法。它的实现方式是:使用Handler进行片段事务处理。该处理程序应该在2000毫秒延迟后被调用。 例如,在调用makeTransaction时。

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

void actuallyMakeTransaction()
{
 // transaction code
}

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

它为我解决了这个问题


1
为什么2秒的延迟是正确的解决方案?你能解释一下吗? - hwrdprkns
抱歉,我自己也不太确定,所以无法解释。不管怎样,它对你有用吗? - DroidBoy
这已经不是1990年了。仅仅等待来修复问题几乎总是一个坏主意。相反,通过Abbath提供的链接http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html自我学习吧。 - Jan Rabe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接