为什么在 Navigation Controller 中实现 Include 时出现 StackOverflowError?

Why I have StackOverflowError when implementing Include in Navigation Controller?

我的项目文件:https://drive.google.com/file/d/11llz7ylWe7ACyLMBbqp6YzugUL8hhImt/view?usp=sharing

所以我有 2 个导航图。称为主导航图和授权图。

我将主图包含到 auth 图中,反之亦然,auth 图在主图中。

我想实现登录系统,所以当用户成功登录后,用户将转到 main activity(有底部导航视图和工具栏),auth activity 没有底部导航视图或片段。这是图表

  1. 主导航图:

<?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. 授权图:

    <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>
    

当在 LoginFragment 中点击登录按钮时,我使用下面的代码:

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

并且在 HomeFragment 中,当注销按钮确实被点击时我使用:

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

但是我遇到了 Whosebugerror:

E/AndroidRuntime: FATAL EXCEPTION: main Process: com.muchammadagunglaksana.navcontroller, PID: 14322 java.lang.WhosebugError: 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 !!!

出了什么问题?

<include> 标签完全等同于 copy/pasting 代替 <include> 的包含图的确切内容。通过让 auth_graph 包含 navigation_graph,您已经构建了一个循环:navigation_graph 包含 auth_graph,其中包含 navigation_graph 永远。

您需要做的是从 auth_graph 中删除 <include app:graph="@navigation/navigation_graph" />。因为您的 auth_graph 已经在 navigation_graph 中,您不需要再次添加它,但您可以直接引用这些目的地中的任何一个。

正如@ianhanniballake 所说,当您使用 <include> 标签时,您会将所有导航图目的地复制到实际目的地中。我遇到了同样的问题,所以我所做的就是这个。我创建了一个 util class,其中我有这个方法:

    /**
     * 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)
        }

    }

所以当你需要添加图表的时候,你可以像这样在代码中添加它:

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

希望对大家有所帮助!