IllegalStateException: Can not perform this action after onSaveInstanc

148 阅读3分钟

 错误信息:

Fatal Exception: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1844)
at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1884)
at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:329)
at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:294)
at androidx.fragment.app.DialogFragment.show(DialogFragment.java:260)

背景:

        在请求某业务Api时,网络请求失败,弹出DialogFragment,比如示例

val builder = CommonConfirmDialog.Builder() val tipDialog = builder.setType(CommonConfirmDialog.TYPE_NO_CANCEL).setCancelOutSide(true) .setShowTitle(false).setBoldContent(true).setContent(errorMsg).build() tipDialog.show(supportFragmentManager, "xxx_tip")

结果在 show的时候发生crash Fatal Exception: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState;

原因分析:

        出现 IllegalStateException 是因为在 onSaveInstanceState 之后尝试执行 Fragment 事务。要解决这个问题,可以采用以下方法之一:出现 IllegalStateException 是因为在 onSaveInstanceState 之后尝试执行 Fragment 事务(如添加、移除或替换Fragment),这会导致状态丢失问题。

 解决建议:

  1. 使用commitAllowingStateLoss()

这会允许状态丢失,但要谨慎使用,因为可能会导致意外问题。

  1. onSaveInstanceState之前执行事务: 确保在onSaveInstanceState方法调用之前完成所有Fragment事务。

  2. 使用Handler或其他方式延迟事务: 使用Handler将事务推迟到onSaveInstanceState之后。

使用 commitAllowingStateLoss() 可能会导致一些意外问题:

  1. 状态丢失:在某些情况下,如果应用程序在提交事务后被系统终止,可能会丢失一些状态或数据。这可能会导致不一致的 UI 状态。
  2. 不一致的用户体验:由于状态可能丢失,用户可能会看到不一致的界面或操作,这可能会导致混淆或不良体验。
  3. 调试困难:由于状态丢失的问题在调试过程中可能不容易发现,因此使用 commitAllowingStateLoss() 可能会使问题更难排查。

    commitAllowingStateLoss() 应该作为最后的手段,只有在其他方法无法解决问题时才使用。

解决办法:

commitAllowingStateLoss()

val builder = CommonConfirmDialog.Builder() val tipDialog = builder.setType(CommonConfirmDialog.TYPE_NO_CANCEL)     .setCancelOutSide(true)     .setShowTitle(false)     .setBoldContent(true)     .setContent(errorMsg)     .build() tipDialog.show(supportFragmentManager, "xxx_tip")

将 show 方法修改为以下形式: 

supportFragmentManager.beginTransaction().add(tipDialog, "xxx_tip").commitAllowingStateLoss()

在 onSaveInstanceState 之前执行事务

确保事务在 onResume() 方法中完成,以确保它在 onSaveInstanceState() 之前执行,调用之前完成对话框的显示。例如,在 onResume 中执行:

override fun onResume() {
super.onResume()
val builder = CommonConfirmDialog.Builder()
val tipDialog = builder.setType(CommonConfirmDialog.TYPE_NO_CANCEL)
.setCancelOutSide(true)
.setShowTitle(false)
.setBoldContent(true)
.setContent(errorMsg)
.build()
tipDialog.show(supportFragmentManager, "xxx_tip")
}

使用 Handler 延迟事务

val builder = CommonConfirmDialog.Builder()
val tipDialog = builder.setType(CommonConfirmDialog.TYPE_NO_CANCEL)
.setCancelOutSide(true)
.setShowTitle(false)
.setBoldContent(true)
.setContent(errorMsg)
.build()

val transaction = supportFragmentManager

val fragment: Fragment? = transaction.findFragmentByTag("xxx_tip")
if (fragment != null) {

 val fragmentTransaction: FragmentTransaction = transaction.beginTransaction()                                                fragmentTransaction.remove(fragment)

 fragmentTransaction.commit()

}

Handler(Looper.getMainLooper()).post {
tipDialog.show(supportFragmentManager, "xxx_tip")
}

commitNow()

commit()`: 事务是异步提交的,意味着它会被排队到主线程的消息队列中,可能会有一定的延迟。

commitNow()`: 事务是同步提交的,意味着它会立即应用,确保事务的操作在代码继续执行之前完成。

commitNow 的好处:

立即应用

使用 commitNow() 可以确保事务会立即生效。这在某些情况下很重要,比如当你需要在继续执行其他操作之前确保 Fragment 事务已经完成时。

避免 UI 问题

如果你在进行一些依赖于 Fragment 的 UI 操作(例如更改 Fragment 的 UI 状态),commitNow() 确保这些更改会立即可见,从而避免了因事务未完成而导致的 UI 问题。

减少潜在问题

在某些复杂的 Fragment 事务管理中,使用 commitNow() 可以帮助确保事务状态在继续执行其他代码之前是一致的,从而减少因事务未提交而导致的潜在问题。

总结:

根据实际情况选择合适的方法来确保Fragment事务在正确的生命周期内执行