当我试图从一个片段导航到另一个片段时,我遇到了新的Android导航架构组件的问题,我得到了这个奇怪的错误:

java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController

其他导航都很好,除了这个。

我使用Fragment的findNavController()函数来访问NavController。

任何帮助都将不胜感激。


当前回答

I caught this exception after some renames of classes. For example: I had classes called FragmentA with @+is/fragment_a in navigation graph and FragmentB with @+id/fragment_b. Then I deleted FragmentA and renamed FragmentB to FragmentA. So after that node of FragmentA still stayed in navigation graph, and android:name of FragmentB's node was renamed path.to.FragmentA. I had two nodes with the same android:name and different android:id, and the action I needed were defined on node of removed class.

其他回答

为了防止崩溃,我采取了如下措施:

我有一个BaseFragment,在那里我添加了这个乐趣,以确保目的地是已知的currentDestination:

fun navigate(destination: NavDirections) = with(findNavController()) {
    currentDestination?.getAction(destination.actionId)
        ?.let { navigate(destination) }
}

值得注意的是,我正在使用SafeArgs插件。

在我的情况下,我有多个导航图文件,我试图从一个导航图位置移动到另一个导航图的目的地。

为此,我们必须将第二个导航图像这样包含在第一个导航图中

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

并将此添加到你的行动中:

<action
        android:id="@+id/action_fragment_to_second_graph"
        app:destination="@id/second_graph" />

其中second_graph为:

<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/second_graph"
    app:startDestination="@id/includedStart">

在第二张图中。

更多信息请点击这里

更新到@AlexNuts回答以支持导航到嵌套图。当一个动作使用一个嵌套图作为目的地时,如下所示:

<action
    android:id="@+id/action_foo"
    android:destination="@id/nested_graph"/>

此操作的目的ID不能与当前目的进行比较,因为当前目的不能是图形。必须解析嵌套图的起始目的地。

fun NavController.navigateSafe(directions: NavDirections) {
    // Get action by ID. If action doesn't exist on current node, return.
    val action = (currentDestination ?: graph).getAction(directions.actionId) ?: return
    var destId = action.destinationId
    val dest = graph.findNode(destId)
    if (dest is NavGraph) {
        // Action destination is a nested graph, which isn't a real destination.
        // The real destination is the start destination of that graph so resolve it.
        destId = dest.startDestination
    }
    if (currentDestination?.id != destId) {
        navigate(directions)
    }
}

然而,这将防止导航到同一目的地两次,这是有时需要的。为了允许这一点,你可以添加一个检查action.navOptions?. shouldlaunchsingletop(),并添加app:launchSingleTop="true"的动作,你不希望重复的目的地。

用try-catch(简单的方法)包装你的导航调用,或者确保在短时间内只有一个导航调用。这个问题可能不会消失。复制更大的代码片段在你的应用程序和尝试。

你好。基于上面的一些有用的回答,我想分享我的解决方案,可以扩展。

下面是导致我的应用程序崩溃的代码:

@Override
public void onListItemClicked(ListItem item) {
    Bundle bundle = new Bundle();
    bundle.putParcelable(SomeFragment.LIST_KEY, item);
    Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
}

一个很容易重现这个错误的方法是用多个手指在项目列表上点击,点击每个项目就会在导航到新屏幕上解决(基本上和人们注意到的一样——在很短的时间内点击两次或两次以上)。我注意到:

第一次导航调用总是正常工作; 第二个和所有其他的导航方法调用在IllegalArgumentException中解析。

在我看来,这种情况可能会经常出现。因为重复代码是一种糟糕的做法,有一点影响总是好的,我想到了下一个解决方案:

public class NavigationHandler {

public static void navigate(View view, @IdRes int destination) {
    navigate(view, destination, /* args */null);
}

/**
 * Performs a navigation to given destination using {@link androidx.navigation.NavController}
 * found via {@param view}. Catches {@link IllegalArgumentException} that may occur due to
 * multiple invocations of {@link androidx.navigation.NavController#navigate} in short period of time.
 * The navigation must work as intended.
 *
 * @param view        the view to search from
 * @param destination destination id
 * @param args        arguments to pass to the destination
 */
public static void navigate(View view, @IdRes int destination, @Nullable Bundle args) {
    try {
        Navigation.findNavController(view).navigate(destination, args);
    } catch (IllegalArgumentException e) {
        Log.e(NavigationHandler.class.getSimpleName(), "Multiple navigation attempts handled.");
    }
}

}

因此上面的代码只改变了一行:

Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);

:

NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);

它甚至变得更短了一点。代码在发生崩溃的确切位置进行了测试。没有经历过,并将使用相同的解决方案为其他导航,以避免同样的错误进一步。

任何想法都欢迎!

到底是什么导致了崩溃

记住,当我们使用方法navigation . findnavcontroller时,我们使用相同的导航图、导航控制器和后堆栈。

We always get the same controller and graph here. When navigate(R.id.my_next_destination) is called graph and back-stack changes almost instantly while UI is not updated yet. Just not fast enough, but that is ok. After back-stack has changed the navigation system receives the second navigate(R.id.my_next_destination) call. Since back-stack has changed we now operate relative to the top fragment in the stack. The top fragment is the fragment you navigate to by using R.id.my_next_destination, but it does not contain next any further destinations with ID R.id.my_next_destination. Thus you get IllegalArgumentException because of the ID that the fragment knows nothing about.

这个确切的错误可以在NavController.java方法findDestination中找到。

当我按了两次后退键时,我想到了这个问题。首先,我拦截KeyListener并覆盖KeyEvent.KEYCODE_BACK。我在名为OnResume的函数中添加了下面的代码,然后解决了这个问题/问题。

  override fun onResume() {
        super.onResume()
        view?.isFocusableInTouchMode = true
        view?.requestFocus()
        view?.setOnKeyListener { v, keyCode, event ->
            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BACK) {
                activity!!.finish()
                true
            }
            false
        }
    }

当我第二次遇到这种情况时,它的状态与第一次相同,我发现我可能会使用add函数。让我们来分析一下这些情况。

首先,FragmentA导航到FragmentB,然后FragmentB导航到FragmentA,然后按下返回按钮…崩溃出现了。 其次,FragmentA导航到FragmentB,然后FragmentB导航到FragmentC, FragmentC导航到FragmentA,然后按下返回按钮…崩溃出现了。

所以我认为当按下返回按钮时,FragmentA会回到FragmentB或FragmentC,这会导致登录混乱。最后,我发现名为popBackStack的函数可以用于返回,而不是导航。

  NavHostFragment.findNavController(this@TeacherCloudResourcesFragment).
                        .popBackStack(
                            R.id.teacher_prepare_lesson_main_fragment,false
                        )

到目前为止,问题已经真正解决了。