I have this scenario on my MainActivity
:
// onCreate
firebaseAuth.addAuthStateListener { firebaseAuth ->
when (firebaseAuth.currentUser) {
null -> {
hideAppBars()
clearBackStack(supportFragmentManager)
showFragment(fragment = LoginOrRegisterFragment())
}
else -> {
showAppBars()
clearBackStack(supportFragmentManager)
showFragment(fragment = HomeFragment())
}
}
}
The clearBastack
is just a method that is popping the full backstack of the Fragments:
private fun clearBackStack(fragmentManager: FragmentManager) {
with(fragmentManager) {
if (backStackEntryCount > 0)
popBackStack()
}
}
And showFragment
method:
fun showFragment(fragment: Fragment, addToBackStack: Boolean = false) {
supportFragmentManager.beginTransaction().apply {
replace(R.id.fragmentContainer, fragment)
if (addToBackStack) addToBackStack(null)
}.commit()
}
In a usual flow, everything goes OK. Hit Login: BackStack clears and from LoginFragment
I get to HomeFragment
. However, if I press back when I'm in the LoginFragment
and resume , I get IllegalStateException: FragmentManager has been destroyed
What seems to fix the issue
Explicitly checking if(!supportFragmentManager.isDestroyed)
:
fun showFragment(fragment: Fragment, addToBackStack: Boolean = false) {
if (!supportFragmentManager.isDestroyed) {
supportFragmentManager.beginTransaction().apply {
replace(R.id.fragmentContainer, fragment)
if (addToBackStack) addToBackStack(null)
}.commit()
}
}
UPDATE: Full stacktrace:
java.lang.IllegalStateException: FragmentManager has been destroyed
at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1725)
at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:321)
at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:286)
at com.coroutinedispatcher.datacrypt.MainActivity.showFragment(MainActivity.kt:57)
at com.coroutinedispatcher.datacrypt.MainActivity.showFragment$default(MainActivity.kt:52)
at com.coroutinedispatcher.datacrypt.MainActivity$onCreate$1.onAuthStateChanged(MainActivity.kt:36)
at com.google.firebase.auth.zzj.run(com.google.firebase:firebase-auth@@19.4.0:3)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at com.google.android.gms.internal.firebase_auth.zzj.dispatchMessage(com.google.firebase:firebase-auth@@19.4.0:6)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Line that throws is supportFragmentManager.apply{bla()}.commit()
.
Question is, why, what is actually happening in the background?
You should remove the AuthStateListnener in onDestroy of Activity.
Although technically you should also consider that this could still trigger fragment transactions after
onStop
, which would cause athis action cannot be performed after onSaveInstanceState
error, so you should actually only handle the navigation action if the Activity is at least started.You could use https://github.com/Zhuinden/live-event for example for that.