【问题标题】:by navGraphViewModels creates java.lang.IllegalArgumentException: No destination with ID <destination id> is on the NavController's back stack由 navGraphViewModels 创建 java.lang.IllegalArgumentException: 没有 ID <destination id> 的目的地在 NavController 的后台堆栈上
【发布时间】:2020-09-07 15:22:06
【问题描述】:

当发生配置更改并因此重新创建我的 Activity 和 Fragment 时,我的导航 Graph 范围内的 ViewModel 不可用,而 Fragments 已经被再次创建。
似乎在 navGraph 之前重新创建了 Fragment。

我正在使用此代码从我的 Fragment 初始化我的 navGraph 范围内的 ViewModel:

private val myViewModel: MyViewModel by navGraphViewModels(R.id.nav_graph_id)

如果我尝试在片段onViewCreated 函数中使用myViewModel,我会在配置更改后得到IllegalArgumentException。例外:

java.lang.IllegalArgumentException: No destination with ID <destination id> is on the NavController's back stack

我该如何处理?

I have already checked that my ID isn't used anywhere else.

编辑1:

这是我的 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
    <androidx.fragment.app.FragmentContainerView
            android:id="@+id/main_nav_host"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:navGraph="@navigation/main_nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

这是我的 main_nav_graph.xml:

<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/main_nav_graph"
        app:startDestination="@id/main_nav_graph_1">
    
    <navigation
            android:id="@+id/main_nav_graph_1"
            android:label="@string/nav_graph_1_label"
            app:startDestination="@id/nav_graph_1_start_fragment">
        <!-- nav graph stuff -->
    </navigation>
    
    <navigation
            android:id="@+id/main_nav_graph_2"
            android:label="@string/nav_graph_2_label"
            app:startDestination="@id/nav_graph_2_start_fragment">
        
        <fragment
                android:id="@+id/nav_graph_2_start_fragment"
                android:label="@string/nav_graph_2_start_fragment_label"
                android:name="my.package.ui.NavGraph2StartFragment"
                tools:layout="@layout/fragment_nav_graph_2_start">
        </fragment>
        <!-- In here is where I get the problem -->
    </navigation>
    
</navigation>

【问题讨论】:

  • 您是否在 XML 中使用 app:navGraph 字段?或者您是在您的NavController 上手动调用setGraph?您可以包含该代码吗?
  • 我已经编辑了我的帖子。这是你想要的代码吗?
  • 您使用的是什么版本的导航和片段?
  • @ianhanniballake 我们遇到了同样的问题,不幸的是,AndroidKotlinNoob 的解决方案对我们不起作用。我们开始在 onViewCreated() 方法中观察视图模型,在活动重新创建之后似乎还为时过早。 NavHostController 包含 mBackStackToRestore 属性中的有效条目,但 mBackStack 数组仍然为空。我们必须等到(不推荐使用的)onActivityCreated() 回调才能使其工作。使用 Fragment 1.3.0-beta01、Activity 1.2.0-beta01 和 Navigation 2.3.1。
  • @harry248 我正在使用 Navigation 2.3.1 和 appcompat 1.2.0 并且没有这个问题。即使我将我的 appcompat 库更新为 1.3.0-alpha02 它仍然有效。也许你应该单独发一篇关于这个的帖子,因为它可能会得到更多的关注。

标签: android kotlin android-navigation-graph


【解决方案1】:

我的问题如下:

在配置更改后,NavGraph 返回到其起始目的地,但最后一次活动的 Fragment 无论如何都会被加载。这意味着实际启动的 Fragment 和 navGraph 的当前目的地不同步。
然后当我的 Fragment 尝试加载 navGraph 范围内的 ViewModel 时,它失败了,因为 navGraph 认为它位于与实际不同的 Fragment 上。

为了解决我的问题,我必须使用活动 savedInstanceState Bundle 保存和恢复 navController 的状态。 (来源:https://stackoverflow.com/a/59987336

override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)
    savedInstanceState.putBundle("nav_state", fragment.findNavController().saveState())
}

// restore in RestoreInstanceState
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)
    fragment.findNavController().restoreState(savedInstanceState.getBundle("nav_state"))
}

// or restore in onCreate
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (savedInstanceState?.containsKey("nav_state") == true) {
        fragment.findNavController().restoreState(savedInstanceState.getBundle("nav_state"))
    }
}

【讨论】:

    【解决方案2】:

    对于其他来到这里的人,我在当前显示一个对话框时遇到了这个问题,我在对话框被关闭之前向使用 by navGraphViewModels() 的 Fragment 发出了 navController.navigate() 调用。

    就好像导航库无法理解在显示对话框时存在具有该 ID 的目的地。

    为了更清楚起见,此对话框在我的导航图 XML 中使用 &lt;dialog&gt; 属性显示。

    我的解决方法(目前)是确保在通过view.postDelayed({ navController.navigate(elsewhere)}, 300) 进一步调用导航到其他地方之前关闭对话框

    这不是我第一次遇到 NavController 在显示对话框时尝试导航的问题。

    【讨论】:

      【解决方案3】:

      通过改变这个很容易解决:

      private val myViewModel: MyViewModel by hiltNavGraphViewModels(R.id.nav_graph_id)
      

      收件人:

      private val viewModel: AuthViewModel by activityViewModels{ defaultViewModelProviderFactory }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-24
        • 2014-11-22
        • 1970-01-01
        • 2020-11-09
        • 2011-06-08
        • 2014-03-10
        相关资源
        最近更新 更多