Agung
Agung

Reputation: 13843

Why I have StackOverflowError when implementing Include in Navigation Controller?

my project file: https://drive.google.com/file/d/11llz7ylWe7ACyLMBbqp6YzugUL8hhImt/view?usp=sharing

so I have 2 navigation graph. called main navigation graph and also auth graph.

I include main graph into auth graph and vice versa, auth graph in main graph.

I want to implement login system, so when the user successfully logged in then the user will go to main activity (that has bottom navigation view and toolbar), auth activity does not have bottom navigation view or fragment. here is the graphs

  1. main navigation graph:

enter image description here

<?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"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/navigation_graph"
            app:startDestination="@id/destination_home">

    <include app:graph="@navigation/auth_graph" />

    <fragment android:id="@+id/destination_home" android:name="com.muchammadagunglaksana.navcontroller.HomeFragment"
              android:label="Home Judul" tools:layout="@layout/fragment_home">
        <action android:id="@+id/action_toAuthActivity" app:destination="@id/auth_graph"/>
    </fragment>


    <fragment android:id="@+id/destination_camera" android:name="com.muchammadagunglaksana.navcontroller.CameraFragment"
              android:label="Camera Judul" tools:layout="@layout/fragment_camera">
        <action android:id="@+id/toPhotosDestination" app:destination="@id/destination_photos"/>
    </fragment>


    <fragment android:id="@+id/destination_photos" android:name="com.muchammadagunglaksana.navcontroller.PhotosFragment"
              android:label="Foto Judul" tools:layout="@layout/fragment_photos">
        <action android:id="@+id/toHomeDestination" app:destination="@id/destination_home"/>
        <argument android:name="numberOfPhotos" app:argType="integer" android:defaultValue="0"/>
    </fragment>


    <fragment android:id="@+id/destination_settings"
              android:name="com.muchammadagunglaksana.navcontroller.SettingsFragment"
              android:label="Setting Judul" tools:layout="@layout/fragment_settings"/>
</navigation>
  1. Auth graph: enter image description here

    <include app:graph="@navigation/navigation_graph" />
    
    <fragment android:id="@+id/loginFragment" android:name="com.muchammadagunglaksana.navcontroller.LoginFragment"
              android:label="fragment_login" tools:layout="@layout/fragment_login">
        <action android:id="@+id/action_toMainActivity" app:destination="@id/navigation_graph"/>
    </fragment>
    

when login button clicked in the LoginFragment then I use the code below:

       login_button.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_toMainActivity)
        }

and also in the HomeFragment, when the logout button did clicked I use:

logout_button.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_toAuthActivity)
        }

but I got stackoverflowerror:

E/AndroidRuntime: FATAL EXCEPTION: main Process: com.muchammadagunglaksana.navcontroller, PID: 14322 java.lang.StackOverflowError: stack size 8MB at android.support.v4.util.SparseArrayCompat.(SparseArrayCompat.java:77) at android.support.v4.util.SparseArrayCompat.(SparseArrayCompat.java:62) at androidx.navigation.NavGraph.(NavGraph.java:44) at androidx.navigation.NavGraphNavigator.createDestination(NavGraphNavigator.java:54) at androidx.navigation.NavGraphNavigator.createDestination(NavGraphNavigator.java:29) at androidx.navigation.NavInflater.inflate(NavInflater.java:100) at androidx.navigation.NavInflater.inflate(NavInflater.java:80) at androidx.navigation.NavInflater.inflate(NavInflater.java:128) at androidx.navigation.NavInflater.inflate(NavInflater.java:80) at androidx.navigation.NavInflater.inflate(NavInflater.java:128) at androidx.navigation.NavInflater.inflate(NavInflater.java:80) at androidx.navigation.NavInflater.inflate(NavInflater.java:128) at androidx.navigation.NavInflater.inflate(NavInflater.java:80) at androidx.navigation.NavInflater.inflate(NavInflater.java:128) at androidx.navigation.NavInflater.inflate(NavInflater.java:80) at androidx.navigation.NavInflater.inflate(NavInflater.java:128)

na.navcontroller E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!

what went wrong ?

Upvotes: 6

Views: 1752

Answers (2)

Dan Ponce
Dan Ponce

Reputation: 646

As @ianhanniballake said when you use an <include> tag you copy all the navgraph destinations into the actual one. I had the same problem so what I did was this. I created a util class where I have this method:

    /**
     * Search all the destinations in
     * the graph to be added. If the
     * actual graph doesn't contain
     * one of these destinations, is
     * added to the actual graph
     *
     * @param view          the actual view (to extract the actual graph and to inflate the new one)
     * @param navGraphId    the graph destinations to be added
     */
    fun addGraphDestinations(view: View, navGraphId : Int) {

        // Get the actual navcontroller
        val navController = view.findNavController()

        // Get the nav inflater
        val navInflater = navController.navInflater

        // Get the actual graph in use
        val actualGraph = navController.graph

        // Inflate the new graph
        val newGraph = navInflater.inflate(navGraphId)

        val list = mutableListOf<NavDestination>()

        // Search if there's a new destination to add into the actual graph
        newGraph.forEach { destination ->

            if(actualGraph.findNode(destination.id) == null) {
                list.add(destination)
            }
        }

        list.forEach {
            newGraph.remove(it)
            actualGraph.addDestination(it)
        }

    }

So when it comes the case where you need to add a graph, you add it in code like this:

    // We have to check if all prospect destinations are already added to the actual graph
    NavigationUtils.addGraphDestinations(view, R.navigation.your_graph)

Hope it helps someone!

Upvotes: 1

ianhanniballake
ianhanniballake

Reputation: 200040

An <include> tag is the exact equivalent of copy/pasting the exact content of the including graph in place of the <include>. By having your auth_graph include the navigation_graph, you've built a loop: navigation_graph contains auth_graph which contains navigation_graph on and on forever.

What you need to do is remove the <include app:graph="@navigation/navigation_graph" /> from your auth_graph. Because your auth_graph is already within the navigation_graph, you don't need to add it a second time, but you can reference any of those destinations directly.

Upvotes: 16

Related Questions