Android Development
IllegalStateException
onSaveInstanceState
Exception Handling
Android Lifecycle

getting exception IllegalStateException Can not perform this action after onSaveInstanceState

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The IllegalStateException: Can not perform this action after onSaveInstanceState crash happens when you try to commit a fragment transaction after the activity has saved its state. Once onSaveInstanceState() runs, the system has taken a snapshot of the UI. Any fragment commits after that point would be lost on restore, so Android throws this exception to prevent silent data loss.

Why This Happens

The Android lifecycle proceeds in this order when going to background:

 
onPause()onStop()onSaveInstanceState()onDestroy()

After onSaveInstanceState() executes, the framework has serialized the fragment back stack. If you call FragmentTransaction.commit() after this point, the transaction cannot be included in the saved state and the system throws:

 
java.lang.IllegalStateException:
  Can not perform this action after onSaveInstanceState

Common triggers:

  • An async callback (network response, timer, broadcast receiver) arrives after the activity is backgrounded
  • Committing fragments in onActivityResult() before the activity state is restored
  • Showing a DialogFragment from a background thread callback

The Problematic Pattern

kotlin
1// DANGEROUS: Network callback may fire after onSaveInstanceState
2fun loadData() {
3    api.fetchUser { user ->
4        // Activity might be stopped by now
5        supportFragmentManager.beginTransaction()
6            .replace(R.id.container, UserFragment.newInstance(user))
7            .commit()  // CRASH if activity state already saved
8    }
9}

Fix 1: Use commitAllowingStateLoss()

Replace commit() with commitAllowingStateLoss() when the transaction is not critical to preserve:

kotlin
supportFragmentManager.beginTransaction()
    .replace(R.id.container, UserFragment.newInstance(user))
    .commitAllowingStateLoss()

This tells Android to proceed even if the state has been saved. The transaction may be lost if the process is killed, but it will not crash. Use this for UI updates that can be reconstructed (like showing cached data).

Fix 2: Check Lifecycle State Before Committing

kotlin
1fun showFragment(fragment: Fragment) {
2    if (!isFinishing && !isDestroyed) {
3        // For API 17+ Activities
4        if (!supportFragmentManager.isStateSaved) {
5            supportFragmentManager.beginTransaction()
6                .replace(R.id.container, fragment)
7                .commit()
8        }
9    }
10}

isStateSaved (added in Support Library 26.0.0) returns true if onSaveInstanceState() has been called.

Fix 3: Use Lifecycle-Aware Components

Use LifecycleOwner to ensure operations only run when the activity is in a valid state:

kotlin
1class MyActivity : AppCompatActivity() {
2
3    fun loadData() {
4        api.fetchUser { user ->
5            // Only commit if lifecycle is at least STARTED
6            if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
7                supportFragmentManager.beginTransaction()
8                    .replace(R.id.container, UserFragment.newInstance(user))
9                    .commit()
10            }
11        }
12    }
13}

Or use lifecycleScope with coroutines to automatically cancel when the lifecycle ends:

kotlin
1class MyActivity : AppCompatActivity() {
2
3    fun loadData() {
4        lifecycleScope.launchWhenStarted {
5            val user = api.fetchUser()  // Suspends, auto-cancelled if stopped
6            supportFragmentManager.beginTransaction()
7                .replace(R.id.container, UserFragment.newInstance(user))
8                .commit()
9        }
10    }
11}

Fix 4: Handle onActivityResult Correctly

onActivityResult() is called before onStart() in some Android versions, which means the state is not yet restored:

kotlin
1override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
2    super.onActivityResult(requestCode, resultCode, data)
3    // DON'T commit fragments here directly
4    // Instead, save the result and commit in onResumeFragments()
5    pendingResult = data
6}
7
8override fun onResumeFragments() {
9    super.onResumeFragments()
10    pendingResult?.let { data ->
11        // Safe to commit here — state is fully restored
12        supportFragmentManager.beginTransaction()
13            .replace(R.id.container, ResultFragment.newInstance(data))
14            .commit()
15        pendingResult = null
16    }
17}

Fix 5: DialogFragment.show() Alternative

DialogFragment.show() calls commit() internally and can crash:

kotlin
1// CRASH-PRONE
2MyDialogFragment().show(supportFragmentManager, "dialog")
3
4// SAFE — use showNow or commitAllowingStateLoss
5fun showDialogSafely(dialog: DialogFragment) {
6    if (!supportFragmentManager.isStateSaved) {
7        dialog.show(supportFragmentManager, "dialog")
8    }
9}
10
11// Or override show() in your DialogFragment
12class SafeDialogFragment : DialogFragment() {
13    override fun show(manager: FragmentManager, tag: String?) {
14        try {
15            val ft = manager.beginTransaction()
16            ft.add(this, tag)
17            ft.commitAllowingStateLoss()
18        } catch (e: IllegalStateException) {
19            // Already saved state, skip showing
20        }
21    }
22}

Common Pitfalls

  • Using commit() in async callbacks: Network calls, handlers, and broadcast receivers can fire after onSaveInstanceState(). Always check the lifecycle state or use commitAllowingStateLoss().
  • Overusing commitAllowingStateLoss(): This silently drops transactions if the process dies. Only use it for non-critical UI updates that can be reconstructed, not for user-initiated navigation.
  • Ignoring onResumeFragments(): Committing in onResume() can still crash because onResume() is called before fragments are fully restored. Use onResumeFragments() instead.
  • Fragment transactions in onCreate() of a recreated Activity: If the activity is being recreated from saved state, fragments are already restored. Adding them again creates duplicates. Check savedInstanceState == null first.
  • Nested fragments: Child fragment managers have their own state saving lifecycle. A parent being saved also saves children, so committing child transactions after the parent's onSaveInstanceState() causes the same crash.

Summary

  • The crash happens when committing fragment transactions after onSaveInstanceState() has serialized the UI state
  • Use commitAllowingStateLoss() for non-critical updates that can be lost on process death
  • Check supportFragmentManager.isStateSaved before committing
  • Use lifecycleScope.launchWhenStarted with coroutines for automatic lifecycle awareness
  • Handle onActivityResult data in onResumeFragments(), not directly in the callback
  • Override DialogFragment.show() to use commitAllowingStateLoss() for safe dialog display

Course illustration
Course illustration

All Rights Reserved.