4

I am currently in a situation where it might be unfeasible to refactor the whole app in order to make a more cohesive whole but the current approach to using Jetpack Navigation is causing crashes.

The app has multiple graphs, for the sake of simplicity let's call them:

Graph A,
Graph B,
Graph C

Graph A is set in the activity layout through app:navGraph and if it remains the only used graph the application can and will restore it's state after process death.

However at certain moments the app programatically switches the graphs by using the navController of the FragmentContainerView and calling setGraph(graphResId: Int) with the new graphs id provided. Before that manually inflating the graph was used as well.

Both approaches of setting the graph result in a crash after the first restart after process death and an exception thrown that says:

 Unable to start activity ComponentInfo{com.appname.dev/com.appname.activity.MainActivity}: java.lang.IllegalStateException: Restoring the Navigation back stack failed: destination com.appname.dev:id/graph_b cannot be found from the current destination null

The exception occurs in MainActivity onCreate's call to super.onCreate()

The only solution in my mind that I can come up with is to include the other graphs in the first graph, use actions to navigate to the other graphs and therefor avoid switching graphs. However due to the way the application has been made that might end up being quite time consuming.

I am wondering if I am missing an important step that could mediate this crash.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
DevBot9000
  • 41
  • 3

1 Answers1

4

Alright, I found a solution and it doesn't need any trivial changes to the code either.

nav_a.xml (or Graph A in your case) should look like this:

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

    <action android:id="@+id/action_to_nav_c"
        app:destination="@id/nav_c"/>

    <action android:id="@+id/action_to_nav_b"
        app:destination="@id/nav_b"/>

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.fragment.MainFragment"
        android:label="@string/home" />

    <include app:graph="@navigation/nav_c" />
    <include app:graph="@navigation/nav_b" />
</navigation>

Notice, the actions action_to_nav_c and action_to_nav_b are NOT inside of any other tag. This way you can navigate them from anywhere without getting any errors.

To navigate to nav_c (Graph C in your case), you can do this inside the host activity in Kotlin with safe args:

navController.navigate(NavADirections.actionToNavC())

Or directly (in Java)

navController.navigate(R.id.action_to_nav_c);

Lastly, your nav_c.xml may look like this:

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

    <fragment
        android:id="@+id/settingsFragment"
        android:name="com.example.fragment.SettingsFragment"
        android:label="@string/settings" />
</navigation>
Veli Tasalı
  • 41
  • 1
  • 3