IllegalStateException:在ViewPager的onSaveInstanceState后不能执行此操作

我在市场上从我的应用程序获得用户报告,交付以下异常:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)

显然这与FragmentManager有关,而我并不使用它。堆栈跟踪没有显示任何我自己的类,所以我不知道这个异常发生在哪里以及如何防止它。

为了记录:我有一个tabhost,在每个选项卡中都有一个在活动之间切换的ActivityGroup。

393633 次浏览

我找到了一个解决这类问题的下流方法。如果出于某种原因(我有时间限制的原因)仍然希望保留ActivityGroups,那么只需实现即可

public void onBackPressed() {}

在你的Activity和做一些back代码在那里。即使旧设备上没有这样的方法,这个方法也会被新设备调用。

请检查我的答案在这里。基本上我只需要:

@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}

不要在saveInstanceState方法上调用super()。这把事情搞砸了……

这是支持包中已知的错误

如果你需要保存实例并添加一些东西到你的outState Bundle,你可以使用以下命令:

@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
}

最后,正确的解决方案是(如评论所示)使用:

transaction.commitAllowingStateLoss();

当添加或执行FragmentTransaction时,会导致Exception

这里有一个不同的解决方法。

使用私有成员变量,你可以将返回的数据设置为一个意图,然后在super.onResume();

像这样:

private Intent mOnActivityResultIntent = null;


@Override
protected void onResume() {
super.onResume();
if(mOnActivityResultIntent != null){
... do things ...
mOnActivityResultIntent = null;
}
}


@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(data != null){
mOnActivityResultIntent = data;
}
}

简单有效的解决方案:

遵循简单的步骤

步骤

步骤1:在各自的片段中覆盖onSaveInstanceState状态。并从中移除super method。

 @Override
public void onSaveInstanceState( Bundle outState ) {


}

步骤2:使用 # EYZ0 < / p >

而不是fragmentTransaction.commit( );,而片段操作。

我得到这个异常时,我按下返回按钮取消意图选择器在我的地图片段活动。 我通过替换onResume(在那里我初始化片段)的代码来解决这个问题,onstart()和应用程序工作正常。

有许多与类似错误消息相关的问题。检查这个特定堆栈跟踪的第二行。这个异常与对FragmentManagerImpl.popBackStackImmediate的调用特别相关。

这个方法调用,像popBackStack一样,如果会话状态已经保存,总是IllegalStateException将失败。检查来源。您无法阻止抛出此异常。

  • 删除对super.onSaveInstanceState的调用没有帮助。
  • 使用commitAllowingStateLoss创建片段是没有帮助的。

以下是我对这个问题的观察:

  • 有一个带有提交按钮的表单。
  • 当单击按钮时,将创建一个对话框并启动异步进程。
  • 用户在过程结束之前单击home键——调用onSaveInstanceState
  • 进程完成后,执行回调并尝试popBackStackImmediate
  • IllegalStateException被抛出。

下面是我解决问题的方法:

由于在回调中不可能避免IllegalStateException, catch &忽略它。

try {
activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
// There's no way to avoid getting this if saveInstanceState has already been called.
}

这足以阻止应用程序崩溃。但现在用户将恢复应用程序,并看到他们认为他们按下的按钮根本没有按下(他们认为)。表单片段仍然显示!

要修复此问题,在创建对话框时,使某些状态表示进程已启动。

progressDialog.show(fragmentManager, TAG);
submitPressed = true;

并将这个状态保存在bundle中。

@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}

不要忘记在onViewCreated中再次加载它

然后,在恢复时,如果之前尝试提交,则回滚这些片段。这可以防止用户返回到一个似乎未提交的表单。

@Override
public void onResume() {
super.onResume();
if (submitPressed) {
// no need to try-catch this, because we are not in a callback
activity.getSupportFragmentManager().popBackStackImmediate(name);
submitPressed = false;
}
}

在显示片段之前检查活动isFinishing(),并注意commitAllowingStateLoss()

例子:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
DummyFragment dummyFragment = DummyFragment.newInstance();
ft.add(R.id.dummy_fragment_layout, dummyFragment);
ft.commitAllowingStateLoss();
}

我也遇到过类似的问题,场景是这样的:

  • 我的活动正在添加/替换列表片段。
  • 每个列表片段都有一个对活动的引用,以便在单击列表项时通知活动(观察者模式)。
  • 每个列表片段在它的onCreate方法中调用setRetainInstance(真正的);

活动onCreate方法是这样的:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
.findFragmentByTag(MAIN_FRAGMENT_TAG);
if (mMainFragment == null) {
mMainFragment = new SelectionFragment();


mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
R.layout.item_main_menu, getResources().getStringArray(
R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
transaction.commit();
}

抛出异常是因为当配置改变时(设备旋转),活动被创建,主片段从片段管理器的历史中检索,同时片段已经有破坏活动的引用

将实现改为这样就解决了问题:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
.findFragmentByTag(MAIN_FRAGMENT_TAG);
if (mMainFragment == null) {
mMainFragment = new SelectionFragment();


mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
R.layout.item_main_menu, getResources().getStringArray(
R.array.main_menu)));
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
transaction.commit();
}
mMainFragment.setOnSelectionChangedListener(this);

您需要在每次创建活动时设置侦听器,以避免片段引用已销毁的活动的旧实例。

我认为使用transaction.commitAllowingStateLoss();不是最好的解决方案。 当activity的配置被改变,并且fragment onSavedInstanceState()被调用,然后你的async callback方法尝试提交fragment时,这个异常将被抛出

简单的解决方案是检查活动是否正在改变配置

例如,检查isChangingConfigurations()

即。

< p > <代码>如果(! isChangingConfigurations ()) { / /提交事务。 代码}< / > < / p >

签出链接

当心,使用transaction.commitAllowingStateLoss()可能会给用户带来糟糕的体验。有关为什么抛出此异常的更多信息,请参见这篇文章

把这个添加到你的活动中

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (outState.isEmpty()) {
// Work-around for a pre-Android 4.2 bug
outState.putBoolean("bug:fix", true);
}
}

我也经历过这个问题,每当你的FragmentActivity的上下文发生变化时(例如屏幕方向被改变等),问题就会发生。因此,最好的修复方法是从FragmentActivity更新上下文。

从支持库24.0.0版本开始,您可以调用FragmentTransaction.commitNow()方法来同步提交事务,而不是调用commit()executePendingTransactions()。正如文档所说,这种方法更好:

调用commitNow比调用commit()之后再调用executePendingTransactions()更可取,因为后者会产生试图提交所有当前挂起的事务的副作用,无论这是否是所需的行为。

不要使用commitAllowingStateLoss(),它只应该用于用户的UI状态可以意外改变的情况。

https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss()

如果事务发生在parentFragment的ChildFragmentManager中,使用 parentFragment.isResume()在外面检查。

if (parentFragment.isResume()) {
DummyFragment dummyFragment = DummyFragment.newInstance();
transaction = childFragmentManager.BeginTransaction();
trans.Replace(Resource.Id.fragmentContainer, startFragment);
}

在我的案例中,我发现的最流畅和最简单的解决方案可能是避免在响应活动结果时从堆栈中弹出有问题的片段。因此,在onActivityResult()中更改这个调用:

popMyFragmentAndMoveOn();

:

new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
popMyFragmentAndMoveOn();
}
}

对我有帮助。

异常在这里抛出(在FragmentActivity中):

@Override
public void onBackPressed() {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
super.onBackPressed();
}
}

FragmentManager.popBackStatckImmediate()中,首先调用FragmentManager.checkStateLoss()。这就是IllegalStateException的原因。参见下面的实现:

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

我通过使用一个标志来标记Activity的当前状态来解决这个问题。以下是我的解决方案:

public class MainActivity extends AppCompatActivity {
/**
* A flag that marks whether current Activity has saved its instance state
*/
private boolean mHasSaveInstanceState;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}


@Override
protected void onSaveInstanceState(Bundle outState) {
mHasSaveInstanceState = true;
super.onSaveInstanceState(outState);
}


@Override
protected void onResume() {
super.onResume();
mHasSaveInstanceState = false;
}


@Override
public void onBackPressed() {
if (!mHasSaveInstanceState) {
// avoid FragmentManager.checkStateLoss()'s throwing IllegalStateException
super.onBackPressed();
}
}
}

每当你试图在你的活动中加载一个片段时,请确保活动处于恢复状态,而不是进入暂停状态。在暂停状态下,您可能会丢失已完成的提交操作。

你可以使用transaction.commitAllowingStateLoss()而不是transaction.commit()来加载片段

创建一个布尔值并检查activity是否不进入onpause状态

@Override
public void onResume() {
super.onResume();
mIsResumed = true;
}


@Override
public void onPause() {
mIsResumed = false;
super.onPause();
}

然后在加载片段时检查

if(mIsResumed){
//load the your fragment
}

这是2017年10月,谷歌制作了Android支持库与新事物称为生命周期组件。它为“onSaveInstanceState后不能执行此操作”的问题提供了一些新的思路。

简而言之:

  • 使用生命周期组件来确定弹出片段的时间是否正确。

带解释的较长版本:

  • 为什么会出现这个问题?

    这是因为您试图使用FragmentManager从您的活动(这将持有您的片段,我想?)提交一个事务为您的片段。通常这看起来像你正在尝试为即将到来的片段做一些事务,同时主机活动已经调用savedInstanceState方法(用户可能碰巧触摸home按钮,所以活动调用onStop(),在我的情况下,这是原因)

    通常这个问题不应该发生——我们总是尝试在一开始就将片段加载到活动中,像onCreate()方法就是一个完美的地方。但有时这个会发生,特别是当你不能决定你将加载哪个片段到那个活动,或者你试图从AsyncTask块加载片段(或任何东西将花费一点时间)。时间,在片段事务真正发生之前,但在活动的onCreate()方法之后,用户可以做任何事情。如果用户按下home键,触发活动的onSavedInstanceState()方法,将会出现can not perform this action崩溃。

    如果有人想深入了解这个问题,我建议他们看看这个博客帖子。它深入源代码层,并解释了很多相关内容。此外,它给出了不应该使用commitAllowingStateLoss()方法来解决这个崩溃的理由(相信我,它对您的代码没有任何好处)

  • 如何修复这个?

    • 我应该使用commitAllowingStateLoss()方法加载片段吗?不,你不应该;

    • 我应该覆盖onSaveInstanceState方法,忽略super方法里面?不,你不应该;

    • 我应该使用神奇的isFinishing内部活动,以检查主机活动是否在片段事务的正确时刻?是的,这个看起来像是正确的方法。

    • 李< / ul > < / >
    • 看看生命周期组件可以做什么。

      基本上,谷歌在AppCompatActivity类(以及您应该在项目中使用的其他几个基类)内部进行了一些实现,这使得确定当前生命周期状态更容易实现。让我们回顾一下我们的问题:为什么会出现这个问题?这是因为我们在错误的时间做了一些事情。所以我们尽量不这样做,这个问题就会消失。

      我为我自己的项目编写了一些代码,下面是我使用LifeCycle所做的事情。我在Kotlin编码。

    val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.
    
    
    fun dispatchFragment(frag: Fragment) {
    hostActivity?.let {
    if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
    showFragment(frag)
    }
    }
    }
    
    
    private fun showFragment(frag: Fragment) {
    hostActivity?.let {
    Transaction.begin(it, R.id.frag_container)
    .show(frag)
    .commit()
    }
    

    如上所示。我将检查主机活动的生命周期状态。对于支持库中的生命周期组件,这可能更具体。代码lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)意味着,如果当前状态至少是onResume,不晚于它?这确保我的方法不会在其他生命状态(如onStop)期间执行。

    • 一切都完成了吗?

      当然不是。我所展示的代码讲述了一些防止应用程序崩溃的新方法。但是如果它确实进入了onStop的状态,那行代码将不会做任何事情,因此在屏幕上不显示任何东西。当用户返回应用程序时,他们将看到一个空屏幕,这是一个空的主机活动,根本没有显示任何片段。这是糟糕的体验(是的,比坠机要好一点)。

      所以在这里我希望有一些更好的东西:应用程序不会崩溃,如果它来到生命状态晚于onResume,事务方法是生命状态意识;此外,在用户回到我们的应用程序后,活动将尝试继续完成该片段事务操作。

      我在这个方法中添加了更多内容:

    class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
    private val hostActivity: FragmentActivity? = _host
    private val lifeCycle: Lifecycle? = _host.lifecycle
    private val profilePendingList = mutableListOf<BaseFragment>()
    
    
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume() {
    if (profilePendingList.isNotEmpty()) {
    showFragment(profilePendingList.last())
    }
    }
    
    
    fun dispatcherFragment(frag: BaseFragment) {
    if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
    showFragment(frag)
    } else {
    profilePendingList.clear()
    profilePendingList.add(frag)
    }
    }
    
    
    private fun showFragment(frag: BaseFragment) {
    hostActivity?.let {
    Transaction.begin(it, R.id.frag_container)
    .show(frag)
    .commit()
    }
    }
    }
    

    我在这个dispatcher类中维护了一个列表,来存储那些没有机会完成事务操作的片段。当用户从主屏幕返回并发现仍然有片段等待启动时,它将转到@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)注释下的resume()方法。现在我认为它应该像我预期的那样工作。

我最终创建了一个基本片段,并使我的应用程序中的所有片段扩展它

public class BaseFragment extends Fragment {


private boolean mStateSaved;


@CallSuper
@Override
public void onSaveInstanceState(Bundle outState) {
mStateSaved = true;
super.onSaveInstanceState(outState);
}


/**
* Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
* would otherwise occur.
*/
public void showAllowingStateLoss(FragmentManager manager, String tag) {
// API 26 added this convenient method
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (manager.isStateSaved()) {
return;
}
}


if (mStateSaved) {
return;
}


show(manager, tag);
}
}

然后,当我试图显示一个片段时,我使用showAllowingStateLoss而不是show

是这样的:

MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

我从这个PR: https://github.com/googlesamples/easypermissions/pull/170/files中想出了这个解决方案

另一个可能的解决办法,我不确定是否在所有情况下都有帮助(起源这里<强> < / >强):

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final View rootView = findViewById(android.R.id.content);
if (rootView != null) {
rootView.cancelPendingInputEvents();
}
}
}

如果你在onActivityResult中做一些FragmentTransaction,你可以做什么,你可以在onActivityResult中设置一些布尔值,然后在onResume中,你可以根据布尔值做你的FragmentTransaction。请参考下面的代码。

@Override
protected void onResume() {
super.onResume;
if(isSwitchFragment){
isSwitchFragment=false;
bottomNavigationView.getTabAt(POS_FEED).select();
}
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) {
isSwitchFragment=true;
}
}

我知道@Ovidiu Latcu有一个公认的答案,但过了一段时间,错误仍然存在。

@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}

Crashlytics仍然给我发送这个奇怪的错误信息。

但是错误现在只发生在版本7+(牛轧糖) 我的解决方案是在fragmentTransaction中使用commitAllowingStateLoss ()而不是commit()。< / p >

这个帖子对commitAllowingStateLoss()很有帮助,并且再也没有片段问题了。

总而言之,这里公认的答案可能适用于牛轧糖之前的安卓版本。

这可能会节省一些人几个小时的搜索时间。 幸福的准则。& lt; 3欢呼< / p >
我也有同样的问题。 这是因为之前的活动被破坏了。 当你支持之前的活动时,它被摧毁了。 我把它放在base activity (WRONG)

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
onCreateDrawerActivity(savedInstanceState);
}

我把它放入onStart,它是正确的

@Override
protected void onStart() {
super.onStart();
SpinnerCustom2.setFragmentManager(getSupportFragmentManager());


}
为了绕过这个问题,我们可以使用导航体系结构组件,它是在谷歌I/O 2018中引入的。 导航体系结构组件简化了Android应用程序中导航的实现

礼貌:# EYZ0

这个问题困扰了我很久,但幸运的是,我想出了一个具体的解决方案。它的详细解释是在这里

使用commitAllowStateloss()可能会防止这种异常,但会导致UI不规范。到目前为止,我们已经了解到,当我们在Activity状态丢失后试图提交一个片段时,会遇到IllegalStateException—所以我们应该延迟事务,直到状态恢复。可以像这样简单地做

声明两个私有布尔变量

 public class MainActivity extends AppCompatActivity {


//Boolean variable to mark if the transaction is safe
private boolean isTransactionSafe;


//Boolean variable to mark if there is any transaction pending
private boolean isTransactionPending;

现在在onPostResume()和onPause中,我们设置和取消设置布尔变量isTransactionSafe。想法是只有当活动在前台时才标记事务安全,这样就不会有无状态的机会。

/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
*/
public void onPostResume(){
super.onPostResume();
isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
*/


public void onPause(){
super.onPause();
isTransactionSafe=false;


}


private void commitFragment(){
if(isTransactionSafe) {
MyFragment myFragment = new MyFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.frame, myFragment);
fragmentTransaction.commit();
}
}

-我们到目前为止所做的将从IllegalStateException保存,但我们的事务将丢失,如果它们在活动移动到后台后完成,有点像commitAllowStateloss()。为了解决这个问题,我们有一个isTransactionPending布尔变量

public void onPostResume(){
super.onPostResume();
isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
if (isTransactionPending) {
commitFragment();
}
}




private void commitFragment(){


if(isTransactionSafe) {
MyFragment myFragment = new MyFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.frame, myFragment);
fragmentTransaction.commit();
isTransactionPending=false;
}else {
/*
If any transaction is not done because the activity is in background. We set the
isTransactionPending variable to true so that we can pick this up when we come back to
foreground
*/
isTransactionPending=true;
}
}

关于@Anthonyeef的回答,这里有一个Java的示例代码:

private boolean shouldShowFragmentInOnResume;


private void someMethodThatShowsTheFragment() {


if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
showFragment();
} else {
shouldShowFragmentInOnResume = true;
}
}


private void showFragment() {
//Your code here
}


@Override
protected void onResume() {
super.onResume();


if (shouldShowFragmentInOnResume) {
shouldShowFragmentInOnResume = false;
showFragment();
}
}

如果你从FragmentActivity继承,你必须调用onActivityResult()中的超类:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
...
}

如果你不这样做,并试图在该方法中显示一个片段对话框,你可能会得到OP的IllegalStateException。(说实话,我不太明白为什么超级调用解决了这个问题。onActivityResult()onResume()之前被调用,所以它仍然不允许显示片段对话框。)

如果你有崩溃与popBackStack()或popBackStackImmediate()方法,请尝试修复:

        if (!fragmentManager.isStateSaved()) {
fragmentManager.popBackStackImmediate();
}

这对我来说也很管用。

在我的情况下,我在一个叫做onActivityResult的覆盖方法中得到了这个错误。在挖掘之后,我才发现可能我需要在之前调用“超级” 我添加了它,它只是工作

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data); //<--- THIS IS THE SUPPER CALL
if (resultCode == Activity.RESULT_OK && requestCode == 0) {
mostrarFragment(FiltroFragment.newInstance())
}


}

也许你只需要在代码之前的任何重写上添加一个“super”。

@Gian Gomen在我的案例中,SUPER解决了问题。这似乎是比commitAllowingStateLoss()更正确的解决方案,因为它解决了问题,而不是隐藏它。

@Override
public void onRequestPermissionsResult(
final int requestCode,
@NonNull final String[] permissions,
@NonNull final int[] grantResults
) {
super.onRequestPermissionsResult(requestCode,permissions, grantResults); //<--- Without this line crash
switch (requestCode) {
case Constants.REQUEST_CODE_PERMISSION_STORAGE:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
onPermissionGranted(Constants.REQUEST_CODE_PERMISSION_STORAGE);
}
break;
}


如果状态保存,请使用remove()而不是popup()。

   private void removeFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.isStateSaved()) {
List<Fragment> fragments = fragmentManager.getFragments();
if (fragments != null && !fragments.isEmpty()) {
fragmentManager.beginTransaction().remove(fragments.get(fragments.size() - 1)).commitAllowingStateLoss();
}
}
}

片段事务不应该在Activity.onStop()之后执行! 检查是否有任何回调可以在onStop()之后执行事务。最好是解决原因,而不是尝试使用.commitAllowingStateLoss()

这样的方法来绕过问题

芬兰湾的科特林扩展

fun FragmentManager?.replaceAndAddToBackStack(
@IdRes containerViewId: Int,
fragment: () -> Fragment,
tag: String
) {
// Find and synchronously remove a fragment with the same tag.
// The second transaction must start after the first has finished.
this?.findFragmentByTag(tag)?.let {
beginTransaction().remove(it).commitNow()
}
// Add a fragment.
this?.beginTransaction()?.run {
replace(containerViewId, fragment, tag)
// The next line will add the fragment to a back stack.
// Remove if not needed.
// You can use null instead of tag, but tag is needed for popBackStack(),
// see https://stackoverflow.com/a/59158254/2914140
addToBackStack(tag)
}?.commitAllowingStateLoss()
}

用法:

val fragment = { SomeFragment.newInstance(data) }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, SomeFragment.TAG)

getFragmentManager()改为getChildFragmentManager()。不要使用parent FragmentManager,尽量使用self。

正如您在崩溃报告中看到的,引发异常的最后一行是

checkStateLoss(FragmentManager.java:1109)

如果你看checkStateLoss的实现

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

所以对我来说一个简单的解决方案是找到你在应用程序中调用的片段管理器的任何方法,最终导致这个方法被调用,并简单地检查是否isStateSaved()在调用该方法之前是假的。对我来说是show()方法。我是这样做的

if (!isStateSaved()) {
myDialog.show(fragmentManager, Tag)
}