web-dev-qa-db-fra.com

java.lang.IllegalArgumentException: la destination de navigation xxx est inconnue de ce NavController

J'ai un problème avec le nouveau composant Android Architecture de navigation lorsque j'essaie de naviguer de d'un fragment à un autre, j'obtiens cette erreur étrange:

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

Chaque autre navigation fonctionne bien, sauf celui-ci.

J'utilise:

findNavContoller()

fonction d'extension de Fragment pour accéder à navControler.

Toute aide serait appréciée.

39
Po10cio

Dans mon cas, si l'utilisateur clique très rapidement deux fois sur la même vue, ce plantage se produira. Vous devez donc implémenter une sorte de logique pour éviter plusieurs clics rapides ... Ce qui est très ennuyant, mais cela semble être nécessaire.

Vous pouvez en savoir plus sur la prévention de ce problème ici: Android Preventing Double Click On A Button

Edit 19/03/2019 : Juste pour clarifier un peu, cet incident n'est pas exclusivement reproductible en "cliquant deux fois sur la même vue très rapidement" . Alternativement, vous pouvez simplement utiliser deux doigts et cliquer sur deux vues (ou plus) en même temps, chaque vue disposant de sa propre navigation. C'est particulièrement facile à faire lorsque vous avez une liste d'éléments. Les informations ci-dessus sur la prévention des clics multiples gèreront ce cas.

32
Charles Madere

Vérifiez currentDestination avant d'appeler, une navigation peut être utile.

Par exemple, si vous avez deux destinations de fragment sur le graphe de navigation fragmentA et fragmentB, et qu’il n’existe qu’une action de fragmentA à fragmentB. appeler navigate(R.id.action_fragmentA_to_fragmentB) donnera IllegalArgumentException alors que vous étiez déjà sur fragmentB. Par conséquent, vous devriez toujours vérifier la currentDestination avant de naviguer.

if (navController.currentDestination?.id == R.id.fragmentA) {
    navController.navigate(R.id.action_fragmentA_to_fragmentB)
}
20
theJian

Vous pouvez vérifier l'action demandée dans la destination actuelle du contrôleur de navigation.

fun NavController.navigateSafe(
        @IdRes resId: Int,
        args: Bundle? = null,
        navOptions: NavOptions? = null,
        navExtras: Navigator.Extras? = null
) {
    val action = currentDestination?.getAction(resId)
    if (action != null) navigate(resId, args, navOptions, navExtras)
}
14
Alex Nuts

Dans mon cas, j’utilisais un bouton de retour personnalisé pour naviguer vers le haut. J'ai appelé onBackPressed() au lieu du code suivant

findNavController(R.id.navigation_Host_fragment).navigateUp()

Ceci a provoqué le IllegalArgumentException. Après l'avoir modifiée pour utiliser la méthode navigateUp() à la place, je n'ai plus eu d'incident.

4
Neil

Cela peut également arriver si vous avez un fragment A avec un ViewPager de fragments B et que vous essayez de naviguer de B à C

Comme dans le ViewPager, les fragments ne sont pas une destination de A, votre graphique ne saurait pas que vous êtes sur B.

Une solution peut être d’utiliser ADirections dans B pour accéder à C

3
AntPachon

Ce que j'ai fait pour empêcher le crash est le suivant:

J'ai un BaseFragment, là j'ai ajouté ceci fun pour m'assurer que le destination est connu par le currentDestination:

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

A noter que j'utilise le plugin SafeArgs .

1
Douglas Kazumi

Dans mon cas, le bogue s’est produit parce que j’avais une action de navigation avec les options Single Top et Clear Task activées après un écran de démarrage.

1

Il semble que vous effaciez la tâche. Une application peut avoir une configuration unique ou une série d’écrans de connexion. Ces écrans conditionnels ne doivent pas être considérés comme la destination de départ de votre application.

https://developer.Android.com/topic/libraries/architecture/navigation/navigation-conditional

1
Patrick

TL; DR Enveloppez vos appels navigate avec try-catch (méthode simple) ou assurez-vous qu'il n'y aura qu'un seul appel de navigate dans un court laps de temps. Ce problème ne va probablement pas disparaître. Copiez le plus gros extrait de code dans votre application et essayez-le.

Salut. Sur la base de quelques réponses utiles ci-dessus, je voudrais partager ma solution qui peut être étendue.

Voici le code qui a provoqué ce blocage dans mon application:

@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);
}

Un moyen de reproduire facilement le bogue consiste à appuyer avec plusieurs doigts sur la liste des éléments pour lesquels cliquer sur chaque élément est résolu lors de la navigation vers le nouvel écran (essentiellement les mêmes que ceux notés - deux clics ou plus sur une très courte période). ). J'ai remarqué ça:

  1. La première invocation navigate fonctionne toujours bien;
  2. La seconde et toutes les autres invocations de la méthode navigate sont résolues dans IllegalArgumentException.

De mon point de vue, cette situation peut apparaître très souvent. La répétition de code étant une mauvaise pratique et il est toujours bon d'avoir un point d'influence, j'ai pensé à la solution suivante:

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.");
    }
}

}

Et donc le code ci-dessus ne change que sur une ligne:

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

pour ça:

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

Il est même devenu un peu plus court. Le code a été testé à l’endroit exact où le crash s’est produit. Je n’ai plus fait l’expérience et utilisera la même solution pour d’autres navigations afin d’éviter davantage la même erreur.

Toutes les pensées sont les bienvenues!

Qu'est-ce qui cause exactement le crash

Rappelez-vous que nous travaillons ici avec le même graphe de navigation, le même contrôleur de navigation et la même pile lorsque nous utilisons la méthode Navigation.findNavController.

Nous avons toujours le même contrôleur et graphique ici. Lorsque navigate(R.id.my_next_destination) est appelé graphique et modifications de pile arrière presque instantanément alors que l'interface utilisateur n'est pas encore mise à jour. Juste pas assez vite, mais ça va. Une fois que la pile arrière a été modifiée, le système de navigation reçoit le deuxième appel navigate(R.id.my_next_destination). Depuis que la pile arrière a changé, nous fonctionnons maintenant par rapport au fragment supérieur de la pile. Le fragment du haut est celui auquel vous accédez en utilisant R.id.my_next_destination, mais il ne contient aucune autre destination avec l'ID R.id.my_next_destination. Ainsi, vous obtenez IllegalArgumentException à cause de l'ID sur lequel le fragment ignore tout.

Cette erreur exacte peut être trouvée dans NavController.Java method findDestination.

0
Jenea Vranceanu

Cela m'est arrivé, mon problème était que je cliquais sur un FAB sur tab item fragment. J'essayais de naviguer d'un fragment d'élément d'onglet vers another fragment.

Mais selon Ian Lake dans cette réponse nous devons utiliser tablayout et viewpager, pas de composant de navigation soutien . Pour cette raison, il n'y a pas de chemin de navigation de tablayout contenant fragment à fragment d'élément de tabulation.

ex:

containing fragment -> tab layout fragment -> tab item fragment -> another fragment

La solution consistait à créer un chemin à partir d'une disposition d'onglet contenant un fragment à un fragment voulu, par exemple: chemin: container fragment -> another fragment

Inconvénient:

  • Le graphique de navigation ne représente plus le flux d'utilisateur avec précision.
0
user158

J'ai attrapé cette exception après avoir renommé des classes. Par exemple: j'avais des classes appelées FragmentA avec @+is/fragment_a dans le graphe de navigation et FragmentB avec @+id/fragment_b. Ensuite, j'ai supprimé FragmentA et renommé FragmentB en FragmentA. Ainsi, après que le nœud de FragmentA soit resté dans le graphe de navigation, et que le nœud de Android:name du FragmentB ait été renommé path.to.FragmentA. J'avais deux nœuds avec le même Android:name et différents Android:id, et l'action dont j'avais besoin était définie sur le nœud de la classe supprimée.

0
VasyaFromRussia

Il m’arrive de penser que j’appuie deux fois sur le bouton Retour. Au début, j'intercepte KeyListener et remplace KeyEvent.KEYCODE_BACK. J'ai ajouté le code ci-dessous dans la fonction nommée OnResume pour le fragment, puis cette question/ce problème est résolu.

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

Lorsque cela m'arrive une seconde fois et que son statut est identique à celui de la première, je constate que j'utilise peut-être la fonction adsurd. Analysons ces situations.

  1. Tout d'abord, FragmentA navigue vers FragmentB, puis FragmentB navigue vers FragmentA, puis appuyez sur le bouton Précédent ... le crash apparaît.

  2. Deuxièmement, FragmentA navigue vers FragmentB, puis FragmentB navigue vers FragmentC, FragmentC navigue vers FragmentA, puis appuyez sur le bouton Précédent ... le crash apparaît.

Je pense donc que lorsque vous appuierez sur le bouton Précédent, FragmentA retournera à FragmentB ou FragmentC, ce qui provoquera un désordre de connexion. Enfin, je trouve que la fonction nommée popBackStack peut être utilisée pour le retour plutôt que pour la navigation.

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

Jusqu'ici, le problème est vraiment résolu.

0
唐德坤