java.lang.IllegalArgumentException:此NavController未知导航目标xxx

Jer*_*for 58 android android-architecture-navigation

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

java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
Run Code Online (Sandbox Code Playgroud)

每个其他导航工作正常,除了这个特定的导航

我用:

findNavContoller()
Run Code Online (Sandbox Code Playgroud)

Fragment的扩展功能可以访问navControler.

任何帮助将不胜感激.

Cha*_*ere 38

在我的情况下,如果用户非常快速地点击两次相同的视图,则会发生此崩溃.因此,您需要实现某种逻辑以防止多次快速点击......这非常烦人,但似乎是必要的.

你可以在这里阅读更多有关防止这种情况的信息:Android防止双击按钮

  • 有关使用2个手指并同时单击2个视图的编辑!这是我的关键,并帮助我轻松地复制了此问题。大量更新信息。 (10认同)
  • 这个解决方案是为了解决真正的问题:导航组件。在较慢的设备上它也容易失败。创建和膨胀一个新片段肯定会花费 200 毫秒以上。延迟之后,在显示片段之前可能会发送第二个单击事件,我们又回到了同样的问题。 (4认同)

小智 30

我为防止崩溃所做的工作如下:

我有一个 BaseFragment,在那里我添加了这个fun以确保它destination被以下的人知道currentDestination

fun navigate(destination: NavDirections) = with(findNavController()) {
    currentDestination?.getAction(destination.actionId)
        ?.let { navigate(destination) }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这应该是公认的答案。接受的答案不支持导航到对话框 (2认同)
  • 我认为这是最好的答案,谢谢 (2认同)

the*_*ian 26

currentDestination致电之前进行检查可能会有所帮助。

例如,如果您在导航图fragmentA和上有两个片段目标fragmentB,并且从fragmentA到仅执行一个操作fragmentB。通话navigate(R.id.action_fragmentA_to_fragmentB)将在IllegalArgumentException您已经开启时产生fragmentB。因此,您应该始终currentDestination在浏览前检查。

if (navController.currentDestination?.id == R.id.fragmentA) {
    navController.navigate(R.id.action_fragmentA_to_fragmentB)
}
Run Code Online (Sandbox Code Playgroud)

  • 图书馆不应该强迫我们进行检查,这确实是荒谬的。 (30认同)
  • 我有一个搜索应用程序,可通过带有参数的操作进行导航。因此,它可以从currentDestination导航到自身。我最终做了同样的事情,除了navController.currentDestination == navController.graph.node。不过感觉有点脏,我觉得我不必这样做。 (3认同)
  • 即使在 iOS 中,有时当您多次按下按钮时会推送多个 ViewController。估计Android和iOS都有这个问题。 (2认同)

Ale*_*uts 23

您可以在导航控制器的当前目标位置检查请求的操作。

UPDATE 添加了用于安全导航的全局操作的用法。

fun NavController.navigateSafe(
        @IdRes resId: Int,
        args: Bundle? = null,
        navOptions: NavOptions? = null,
        navExtras: Navigator.Extras? = null
) {
    val action = currentDestination?.getAction(resId) ?: graph.getAction(resId)
    if (action != null && currentDestination?.id != action.destinationId) {
        navigate(resId, args, navOptions, navExtras)
    }
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*hon 21

如果您有一个带有 Fragment B 的 ViewPager 的 Fragment A 并且您尝试从 B 导航到 C,也可能发生这种情况

由于在 ViewPager 中,片段不是 A 的目的地,因此您的图表不会知道您在 B 上。

一个解决方案可以是在 B 中使用 ADirections 导航到 C


Jen*_*anu 10

TL;DRnavigatetry-catch(简单的方法)包裹你的电话,或者确保navigate在短时间内只有一个电话。这个问题可能不会消失。在您的应用中复制更大的代码片段并试用。

你好。基于以上几个有用的回复,我想分享我可以扩展的解决方案。

这是在我的应用程序中导致此崩溃的代码:

@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);
}
Run Code Online (Sandbox Code Playgroud)

一种轻松重现错误的方法是用多个手指点击项目列表,点击每个项目会在导航到新屏幕中解析(与人们注意到的基本相同 - 在很短的时间内点击两次或多次)。我注意到:

  1. 第一次navigate调用总是工作正常;
  2. 第二次和该navigate方法的所有其他调用都在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.");
    }
}
Run Code Online (Sandbox Code Playgroud)

}

因此,上面的代码仅在一行中更改:

Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
Run Code Online (Sandbox Code Playgroud)

对此:

NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);
Run Code Online (Sandbox Code Playgroud)

它甚至变得更短了一点。代码在崩溃发生的确切位置进行了测试。没有再体验过,其他导航会用同样的方案,避免进一步出现同样的错误。

欢迎任何想法!

究竟是什么导致了崩溃

请记住,在这里,当我们使用 method 时,我们使用相同的导航图、导航控制器和后台堆栈Navigation.findNavController

我们总是在这里得到相同的控制器和图形。当UI 尚未更新时,何时navigate(R.id.my_next_destination)调用图形和后台堆栈几乎会立即更改。只是不够快,但没关系。在 back-stack 改变后,导航系统接收第二个navigate(R.id.my_next_destination)调用。由于 back-stack 发生了变化,我们现在相对于堆栈中的顶部片段进行操作。顶部片段是您使用 导航到的片段R.id.my_next_destination,但它不包含接下来任何带有 ID 的其他目的地R.id.my_next_destination。因此,您得到的IllegalArgumentException是片段一无所知的 ID。

这个确切的错误可以在NavController.javamethod 中找到findDestination


Abn*_*cio 9

试试看

  1. 创建此扩展函数(或普通函数):

更新(没有反射和更具可读性)

import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.FragmentNavigator

fun Fragment.safeNavigateFromNavController(directions: NavDirections) {
    val navController = findNavController()
    val destination = navController.currentDestination as FragmentNavigator.Destination
    if (javaClass.name == destination.className) {
        navController.navigate(directions)
    }
}
Run Code Online (Sandbox Code Playgroud)

旧(带反射)

import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.FragmentNavigator

inline fun <reified T : Fragment> NavController.safeNavigate(directions: NavDirections) {
    val destination = this.currentDestination as FragmentNavigator.Destination
    if (T::class.java.name == destination.className) {
        navigate(directions)
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 并从您的 Fragment 中像这样使用:
val direction = FragmentOneDirections.actionFragmentOneToFragmentTwo()
// new usage
safeNavigateFromNavController(direction)

// old usage
// findNavController().safeNavigate<FragmentOne>(action)
Run Code Online (Sandbox Code Playgroud)

我的问题是

我有一个片段 (FragmentOne),它转到另外两个片段(FragmentTwo 和 FragmentThree)。在一些低端设备中,用户按下重定向到 FragmentTwo 的按钮,但在用户按下重定向到 FragmentThree 的按钮后的几毫秒内。结果是:

致命异常:java.lang.IllegalArgumentException 无法从当前目标 Destination(fragmentThree) class=FragmentThree 中找到导航操作/目标 action_fragmentOne_to_fragmentTwo

我的解决方法是:

我检查当前目的地是否属于当前片段。如果为真,我执行导航动作。

就这些!


fre*_*yle 6

就我而言,当我将片段中的一个viewpager片段作为viewpager. 的viewpager片段(这是母体片段)在导航XML中加入,但是在不添加动作viewpager父片段。

nav.xml
//reused fragment
<fragment
    android:id="@+id/navigation_to"
    android:name="com.package.to_Fragment"
    android:label="To Frag"
    tools:layout="@layout/fragment_to" >
    //issue got fixed when i added this action to the viewpager parent also
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>
....
// viewpager parent fragment
<fragment
    android:id="@+id/toViewAll"
    android:name="com.package.ViewAllFragment"
    android:label="to_viewall_fragment"
    tools:layout="@layout/fragment_view_all">
Run Code Online (Sandbox Code Playgroud)

通过将操作添加到父 viewpager 片段来修复该问题,如下所示:

nav.xml
//reused fragment
<fragment
    android:id="@+id/navigation_to"
    android:name="com.package.to_Fragment"
    android:label="To Frag"
    tools:layout="@layout/fragment_to" >
    //issue got fixed when i added this action to the viewpager parent also
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>
....
// viewpager parent fragment
<fragment
    android:id="@+id/toViewAll"
    android:name="com.package.ViewAllFragment"
    android:label="to_viewall_fragment"
    tools:layout="@layout/fragment_view_all"/>
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>
Run Code Online (Sandbox Code Playgroud)


Ser*_*aka 6

今天

def navigationVersion = "2.2.1"

问题仍然存在。我对 Kotlin 的做法是:

// To avoid "java.lang.IllegalArgumentException: navigation destination is unknown to this NavController", se more /sf/ask/3574253371/
fun NavController.navigateSafe(
    @IdRes destinationId: Int,
    navDirection: NavDirections,
    callBeforeNavigate: () -> Unit
) {
    if (currentDestination?.id == destinationId) {
        callBeforeNavigate()
        navigate(navDirection)
    }
}

fun NavController.navigateSafe(@IdRes destinationId: Int, navDirection: NavDirections) {
    if (currentDestination?.id == destinationId) {
        navigate(navDirection)
    }
}
Run Code Online (Sandbox Code Playgroud)


Nei*_*eil 5

就我而言,我使用自定义的后退按钮进行导航。我onBackPressed()代替以下代码进行了调用

findNavController(R.id.navigation_host_fragment).navigateUp()
Run Code Online (Sandbox Code Playgroud)

这导致IllegalArgumentException发生。在将其改为使用该navigateUp()方法后,我再也没有崩溃。

  • 我同意这确实很疯狂。我在android导航体系结构组件中遇到的许多事情都感到有些疯狂,因为它设置得过于严格。考虑为项目做我自己的实现,因为这会造成很多麻烦 (2认同)

fre*_*yle 5

就我而言,我有多个导航图文件,并且我试图从 1 个导航图位置移动到另一个导航图中的目的地。

为此,我们必须将第二个导航图包含在第一个导航图中,如下所示

<include app:graph="@navigation/included_graph" />
Run Code Online (Sandbox Code Playgroud)

并将其添加到您的操作中:

<action
        android:id="@+id/action_fragment_to_second_graph"
        app:destination="@id/second_graph" />
Run Code Online (Sandbox Code Playgroud)

哪里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">
Run Code Online (Sandbox Code Playgroud)

在第二张图中。

更多信息请点击此处


Fra*_*ank 5

您可以在导航之前检查请求导航的 Fragment 是否仍然是当前目的地(取自此 gist )

\n

它基本上在片段上设置一个标签以供以后查找。

\n
/**\n * Returns true if the navigation controller is still pointing at \'this\' fragment, or false if it already navigated away.\n */\nfun Fragment.mayNavigate(): Boolean {\n\n    val navController = findNavController()\n    val destinationIdInNavController = navController.currentDestination?.id\n    val destinationIdOfThisFragment = view?.getTag(R.id.tag_navigation_destination_id) ?: destinationIdInNavController\n\n    // check that the navigation graph is still in \'this\' fragment, if not then the app already navigated:\n    if (destinationIdInNavController == destinationIdOfThisFragment) {\n        view?.setTag(R.id.tag_navigation_destination_id, destinationIdOfThisFragment)\n        return true\n    } else {\n        Log.d("FragmentExtensions", "May not navigate: current destination is not the current fragment.")\n        return false\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

R.id.tag_navigation_destination_id只是您必须添加到 ids.xml 中的一个 id,以确保它是唯一的。<item name="tag_navigation_destination_id" type="id" />

\n

有关错误和解决方案以及navigateSafe(...)扩展方法的更多信息,请参阅“修复可怕的 \xe2\x80\x9c\xe2\x80\xa6 对于此 NavController\xe2\x80\x9d 来说是未知的”

\n


Eur*_*tré 3

就我而言,出现错误是因为我在启动屏幕后启用了导航操作Single Top和选项。Clear Task

  • 但clearTask已被弃用,您应该使用popUpTo()代替。 (2认同)

归档时间:

查看次数:

16474 次

最近记录:

6 年,4 月 前