Search code examples
androidandroid-architecture-componentsandroid-architecture-navigation

Navigation Architecture Component - Unable to navigate to fragment from onResume


I'm testing this new library out and want to navigate to a login fragment if the user isn't signed in. This check is done in in a base fragment like:

abstract class SignedInFragment : Fragment() {

override fun onResume() {
    super.onResume()
    if (FirebaseAuth.getInstance().currentUser == null) {
        NavHostFragment.findNavController(this /* Fragment */)
                .navigate(R.id.action_login)
    }
}

}

But this just ends up in a RuntimeException saying FragmentManager is already executing transactions:

    java.lang.RuntimeException: Unable to resume activity {me.hammarstrom.loco/me.hammarstrom.loco.MainActivity}: java.lang.IllegalStateException: FragmentManager is already executing transactions
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3645)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3685)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2898)
    at android.app.ActivityThread.-wrap11(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
    at android.os.Handler.dispatchMessage(Handler.java:105)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6541)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

The navigation graph looks as following:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/mainFragment">

<fragment
    android:id="@+id/loginFragment"
    android:name="me.hammarstrom.loco.ui.login.LoginFragment"
    android:label="LoginFragment" >
    <action
        android:id="@+id/action_loginFragment_to_signedInFragment"
        app:destination="@id/signedInFragment"  />
</fragment>
<fragment
    android:id="@+id/signedInFragment"
    android:name="me.hammarstrom.loco.ui.common.SignedInFragment"
    android:label="SignedInFragment" />
<fragment
    android:id="@+id/mainFragment"
    android:name="me.hammarstrom.loco.ui.main.MainFragment"
    android:label="MainFragment" />
<action
    android:id="@+id/action_login"
    app:destination="@id/loginFragment" />

Should this kind of navigation be done in some other way? Am I missing something?


Solution

  • Looks like a bug in Android Architecture Components. I created a bug report regarding this issue here. I've found that temporary workaround for it is to delay the call to the navigation method so that the fragment manager can execute pending transactions.

    val executor = Executors.newSingleThreadScheduledExecutor()
    executor.schedule({
            activity?.runOnUiThread { NavHostFragment.findNavController(this)
                .navigate(R.id.action_login) }
    }, 1, TimeUnit.MILLISECONDS)
    

    or if you are using coroutines

    launch(UI) {
        delay(1L)
        NavHostFragment.findNavController(this).navigate(R.id.action_login)
    }
    

    UPDATE:

    This issue has been fixed in version 1.0.0-alpha02.