TL; DR Enveloppez vos navigate
appels avec try-catch
(manière simple), ou assurez-vous qu'il n'y aura qu'un seul appel navigate
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:
@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 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:
- La première
navigate
invocation fonctionne toujours bien;
- La seconde et toutes les autres invocations de la
navigate
méthode se résolvent en IllegalArgumentException
.
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:
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 dans une ligne de ceci:
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ù 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ème navigate(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 vers R.id.my_next_destination
lequel vous naviguez en utilisant , mais il ne contient pas d'autres destinations avec ID R.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éthode findDestination
.