我有时会看到以下用于提交的堆栈跟踪,当用户未查看活动时(状态已保存后)可能会发生提交:
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中存储类变量)以检查片段是否将在不合需要的状态下提交,这样我可以存储事务稍后在适当的时候进行提交。
答案 0 :(得分:38)
从支持库版本26.0.0 Beta 1开始,FragmentManager
和Fragment
类中提供了一个新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