Reputation: 13843
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
<?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>
<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
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
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