J'ai un problème avec le nouveau composant Architecture de navigation Android lorsque j'essaie de naviguer d' un fragment à un autre , j'obtiens cette erreur étrange:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
Toute autre navigation fonctionne bien sauf celle-ci.
J'utilise la findNavController()
fonction de Fragment pour accéder au fichier NavController
.
Toute aide serait appréciée.
Réponses:
Dans mon cas, si l'utilisateur clique deux fois très rapidement sur la même vue, ce plantage se produira. Il faut donc implémenter une sorte de logique pour éviter plusieurs clics rapides ... Ce qui est très ennuyeux, mais cela semble nécessaire.
Vous pouvez en savoir plus sur la prévention de cela ici: Android Empêcher le double-clic sur un bouton
Edit 19/03/2019 : Juste pour clarifier un peu plus, ce crash n'est pas exclusivement reproductible en "cliquant sur la même vue deux fois très très rapidement". Alternativement, vous pouvez simplement utiliser deux doigts et cliquer sur deux (ou plus) vues en même temps, où chaque vue a sa propre navigation qu'elle effectuerait. 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 traiteront ce cas.
Edit 16/04/2020 : Juste au cas où vous ne seriez pas très intéressé par la lecture de ce post de Stack Overflow ci-dessus, j'inclus ma propre solution (Kotlin) que j'utilise depuis longtemps maintenant.
OnSingleClickListener.kt
ViewExt.kt
AccueilFragment.kt
la source
Vérifiez
currentDestination
avant d'appeler naviguer peut être utile.Par exemple, si vous avez deux destinations de fragment sur le graphique de navigation
fragmentA
etfragmentB
, et qu'il n'y a qu'une seule action defragmentA
àfragmentB
. appelnavigate(R.id.action_fragmentA_to_fragmentB)
aura pour résultatIllegalArgumentException
lorsque vous étiez déjà surfragmentB
. Par conséquent, vous devez toujours vérifier lecurrentDestination
avant de naviguer.la source
Vous pouvez vérifier l'action demandée dans la destination actuelle du contrôleur de navigation.
UPDATE a ajouté l'utilisation des actions globales pour une navigation sûre.
la source
currentDestination
liste d'actions de. Supposons que vous ayez défini une action globale et que vous utilisiez cette action pour naviguer. Cela échouera car l'action n'est pas définie dans la liste <action> de currentDestination. L'ajout d'un chèque comme celui-cicurrentDestination?.getAction(resId) != null || currentDestination?.id != resId
devrait le résoudre, mais peut également ne pas couvrir tous les cas.?: graph.getAction(resId)
->currentDestination?.getAction(resId)
renverra une action pour les actions globales ou non globales (je l'ai testé). Aussi, serait mieux si vous UTILISE Safe args -> plutôt passernavDirections: NavDirections
queresId
etargs
séparément.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 en B pour accéder à C
la source
(parentFragment as? XActionListener)?.Xaction()
et notez que vous pouvez conserver cette fonction en tant que variable locale si cela est utileCe que j'ai fait pour éviter le crash est le suivant:
J'ai un BaseFragment, là-dedans, j'ai ajouté ceci
fun
pour m'assurer que ledestination
est connu par lecurrentDestination
:À noter que j'utilise le plugin SafeArgs .
la source
Dans mon cas, j'utilisais un bouton de retour personnalisé pour naviguer vers le haut. J'ai appelé
onBackPressed()
à la place du code suivantCela a provoqué le
IllegalArgumentException
. Après l'avoir changé pour utiliser lanavigateUp()
méthode à la place, je n'ai plus eu de plantage.la source
TL; DR Enveloppez vos
navigate
appels avectry-catch
(manière simple), ou assurez-vous qu'il n'y aura qu'un seul appelnavigate
dans un court laps de temps. Ce problème ne disparaîtra probablement pas. Copiez un extrait de code plus volumineux dans votre application et essayez.Bonjour. 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 plantage dans mon application:
Un moyen de reproduire facilement le bogue consiste à appuyer avec plusieurs doigts sur la liste des éléments où le clic sur chaque élément se résout dans la navigation vers le nouvel écran (fondamentalement le même que celui noté par les gens - deux clics ou plus dans un laps de temps très court. ). J'ai remarqué ça:
navigate
invocation fonctionne toujours bien;navigate
méthode se résolvent enIllegalArgumentException
.De mon point de vue, cette situation peut apparaître très souvent. Comme la répétition du code est une mauvaise pratique et qu'il est toujours bon d'avoir un point d'influence, j'ai pensé à la solution suivante:
}
Et donc le code ci-dessus ne change que dans une ligne de ceci:
pour ça:
Il est même devenu un peu plus court. Le code a été testé à l'endroit exact où l'accident s'est produit. Je n'en 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 qu'ici, nous travaillons avec le même graphique de navigation, contrôleur de navigation et back-stack lorsque nous utilisons la méthode
Navigation.findNavController
.Nous obtenons toujours le même contrôleur et graphique ici. Quand
navigate(R.id.my_next_destination)
est appelé graphique et back-stack change presque instantanément alors que l'interface utilisateur n'est pas encore mise à jour. Pas assez vite, mais ça va. Une fois que la pile arrière a changé, le système de navigation reçoit le deuxièmenavigate(R.id.my_next_destination)
appel. Depuis que le back-stack a changé, nous opérons maintenant par rapport au fragment supérieur de la pile. Le fragment supérieur est le fragment versR.id.my_next_destination
lequel vous naviguez en utilisant , mais il ne contient pas d'autres destinations avec IDR.id.my_next_destination
. Ainsi, vous obtenez àIllegalArgumentException
cause de l'ID dont le fragment ne sait rien.Cette erreur exacte peut être trouvée dans la
NavController.java
méthodefindDestination
.la source
Dans mon cas, le problème s'est produit lorsque j'avais réutilisé l'un de mes fragments à l'intérieur d'un
viewpager
fragment en tant qu'enfant duviewpager
. Leviewpager
fragment (qui était le fragment parent) a été ajouté dans le xml de navigation, mais l'action n'a pas été ajoutée dans leviewpager
fragment parent.Correction du problème en ajoutant l'action au fragment de viewpager parent également comme indiqué ci-dessous:
la source
Aujourd'hui
Le problème existe toujours. Mon approche sur Kotlin est:
la source
Vous pouvez vérifier avant de naviguer si le fragment demandant la navigation est toujours la destination actuelle, tirée de cet élément essentiel .
Il définit essentiellement une balise sur le fragment pour une recherche ultérieure.
R.id.tag_navigation_destination_id
est juste un identifiant que vous devrez ajouter à votre ids.xml, pour vous assurer qu'il est unique.<item name="tag_navigation_destination_id" type="id" />
Plus d'informations sur le bogue et la solution, et les
navigateSafe(...)
méthodes d'extention dans "Correction du redouté"… est inconnu de ce NavController "la source
NAV_DESTINATION_ID
quelque chose comme ce stackoverflow.com/a/15021758/1572848R.id
.R.id.tag_navigation_destination_id
est juste un identifiant que vous devrez ajouter à votre ids.xml, pour vous assurer qu'il est unique.<item name="tag_navigation_destination_id" type="id" />
Dans mon cas, j'avais plusieurs fichiers de graphique de navigation et j'essayais de passer d'un emplacement de graphique de navigation à une destination dans un autre graphique de navigation.
Pour cela, nous devons inclure le 2ème graphique de navigation dans le 1er comme celui-ci
et ajoutez ceci à votre action:
où
second_graph
est:dans le deuxième graphique.
Plus d'infos ici
la source
J'ai résolu le même problème en mettant un chèque avant de naviguer au lieu du code standard pour cliquer instantanément sur le contrôle
selon cette réponse
https://stackoverflow.com/a/56168225/7055259
la source
Dans mon cas, le bogue est survenu parce que j'avais une action de navigation avec
Single Top
lesClear Task
options et activées après un écran de démarrage.la source
J'ai eu cette même erreur parce que j'ai utilisé un tiroir de navigation et
getSupportFragmentManager().beginTransaction().replace( )
en même temps quelque part dans mon code.Je me suis débarrassé de l'erreur en utilisant cette condition (tester si la destination):
Dans mon cas, l'erreur précédente a été déclenchée lorsque je cliquais sur les options du panneau de navigation. Fondamentalement, le code ci-dessus a masqué l'erreur, car dans mon code quelque part, j'ai utilisé la navigation en utilisant
getSupportFragmentManager().beginTransaction().replace( )
la condition -n'a jamais été atteint car il
(Navigation.findNavController(v).getCurrentDestination().getId()
était toujours en contact avec le fragment de la maison. Vous ne devez utiliser queNavigation.findNavController(v).navigate(R.id.your_action)
les fonctions du contrôleur de graphique de navigation ou pour toutes vos actions de navigation.la source
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
la source
J'ai attrapé cette exception après quelques changements de noms de classes. Par exemple: j'ai eu des cours appelés
FragmentA
avec@+is/fragment_a
dans le graphique de navigation etFragmentB
avec@+id/fragment_b
. Ensuite , je suppriméFragmentA
et retitréFragmentB
àFragmentA
. Ainsi , après ce nœud deFragmentA
toujours resté dans le graphique de la navigation etandroid:name
duFragmentB
nœud de » a été renommépath.to.FragmentA
. J'avais deux nœuds identiquesandroid:name
et différentsandroid:id
, et l'action dont j'avais besoin était définie sur le nœud de la classe supprimée.la source
Cela me vient à l'esprit lorsque j'appuie deux fois sur le bouton de retour. Au début, j'intercepte
KeyListener
et passe outreKeyEvent.KEYCODE_BACK
. J'ai ajouté le code ci-dessous dans la fonction nomméeOnResume
pour le fragment, puis cette question / problème est résolu.Quand cela m'arrive une deuxième fois et que son statut est le même que le premier, je trouve que j'utilise peut-être le
adsurd
fonction. Analysons ces situations.Tout d'abord, FragmentA navigue vers FragmentB, puis FragmentB navigue vers FragmentA, puis appuyez sur le bouton Retour ... le crash apparaît.
Deuxièmement, FragmentA navigue vers FragmentB, puis FragmentB navigue vers FragmentC, FragmentC navigue vers FragmentA, puis appuyez sur le bouton Retour ... le crash apparaît.
Donc, je pense qu'en appuyant sur le bouton Retour, FragmentA reviendra à FragmentB ou FragmentC, puis cela provoquera le désordre de connexion. Enfin je trouve que la fonction nommée
popBackStack
peut être utilisée pour revenir plutôt que pour naviguer.Jusqu'à présent, le problème est vraiment résolu.
la source
Il semble que le mélange du contrôle fragmentManager de la backstack et du contrôle de l'architecture de navigation de la backstack puisse également causer ce problème.
Par exemple, l'exemple de base CameraX d'origine a utilisé la navigation dans la pile d'arrière-plan fragmentManager comme ci-dessous et il semble qu'il n'interagissait pas correctement avec la navigation:
Si vous enregistrez la `` destination actuelle '' avec cette version avant de passer du fragment principal (le fragment de caméra dans ce cas) et que vous la consignez à nouveau lorsque vous revenez au fragment principal, vous pouvez voir à partir de l'id dans les journaux que l'id ce n'est pas la même chose. À une supposition, la navigation l'a mis à jour lors du déplacement vers le fragment et le fragmntManager ne l'a pas mis à jour à nouveau lors du recul. À partir des journaux:
La version mise à jour de l'exemple de base de CameraX utilise Navigation pour revenir comme ceci:
Cela fonctionne correctement et les journaux affichent le même identifiant lorsqu'ils reviennent au fragment principal.
Je soupçonne que la morale de l'histoire, du moins en ce moment, est d'être très prudent en mélangeant la navigation avec la navigation fragmentManager.
la source
Une manière ridicule mais très puissante est: Appelez simplement ceci:
Créez simplement cette extension:
la source
Il peut y avoir plusieurs raisons à ce problème. Dans mon cas, j'utilisais le modèle MVVM et j'observais un booléen pour la navigation lorsque le booléen est vrai -> naviguer ailleurs ne fait rien et cela fonctionnait bien mais il y avait une erreur ici
lorsque j'appuyais sur le bouton de retour à partir du fragment de destination, je rencontrais le même problème.Et le problème était l'objet booléen car j'ai oublié de changer la valeur booléenne en faux, cela a créé le désordre.Je viens de créer une fonction dans viewModel pour changer sa valeur en faux et l'a appelé juste après le findNavController ()
la source
Habituellement, lorsque cela m'arrive, j'ai eu le problème décrit par Charles Madere: deux événements de navigation déclenchés sur la même interface utilisateur, l'un modifiant la destination actuelle et l'autre échouant car la destination actuelle est modifiée. Cela peut se produire si vous appuyez deux fois ou cliquez sur deux vues avec un écouteur de clic appelant findNavController.navigate.
Donc, pour résoudre ce problème, vous pouvez utiliser if-checks, try-catch ou si vous êtes intéressé, il existe un findSafeNavController () qui effectue cette vérification pour vous avant de naviguer. Il dispose également d'une vérification des peluches pour vous assurer de ne pas oublier ce problème.
GitHub
Article détaillant le problème
la source
Si vous cliquez trop rapidement, cela entraînera une nullité et un crash.
Nous pouvons utiliser RxBinding lib pour vous aider. Vous pouvez ajouter une accélération et une durée au clic avant qu'il ne se produise.
Ces articles sur la limitation sur Android peuvent vous aider. À votre santé!
la source
Si vous utilisez un recyclerview, ajoutez simplement un temps de recharge d'écoute de clic sur votre clic et également dans votre utilisation du fichier xml de recyclerview
android:splitMotionEvents="false"
la source
Après avoir réfléchi aux conseils de Ian Lake dans ce fil Twitter, j'ai proposé l'approche suivante. Ayant
NavControllerWrapper
défini comme tel:Puis dans le code de navigation:
la source
Cela m'est arrivé, mon problème était que je cliquais sur un FAB
tab item fragment
. J'essayais de naviguer de l'un des fragments d'élément d'onglet versanother fragment
.Mais selon Ian Lake dans cette réponse, nous devons utiliser
tablayout
etviewpager
, aucun support de composant de navigation . Pour cette raison, il n'y a pas de chemin de navigation entre le tablayout contenant le fragment et le fragment d'élément d'onglet.ex:
La solution était de créer un chemin à partir de la mise en page de l'onglet contenant le fragment vers le fragment voulu ex: chemin:
container fragment -> another fragment
Désavantage:
la source
Dans mon cas, j'ai eu cette erreur en essayant de naviguer à partir d'un autre thread, dans 50% des cas. Exécuter le code sur le thread principal aide
la source
Dans mon cas, cela s'est produit lorsque j'ai accidentellement ajouté une
+
destination en action, et le crash ne s'est produit que lorsque je suis allé plusieurs fois sur le même fragment.La solution consiste à supprimer
+
de la destination de l'action, à utiliser uniquement à la@id/profileFragment
place de@+id/profileFragment
la source
Mise à jour de la solution @Alex Nuts
S'il n'y a aucune action pour un fragment particulier et que vous souhaitez accéder au fragment
la source
J'ai écrit ces extensions
la source
J'ai créé cette fonction d'extension pour Fragment:
la source