TL; DR Envuelva sus navigate
llamadas con try-catch
(forma simple), o asegúrese de que solo haya una llamada navigate
en un corto período de tiempo. Es probable que este problema no desaparezca. Copie un fragmento de código más grande en su aplicación y pruébelo.
Hola. Basado en un par de respuestas útiles anteriores, me gustaría compartir mi solución que se puede ampliar.
Aquí está el código que causó este bloqueo en mi aplicación:
@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);
}
Una forma de reproducir fácilmente el error es tocar con varios dedos en la lista de elementos donde el clic en cada elemento se resuelve en la navegación a la nueva pantalla (básicamente lo mismo que la gente notó: dos o más clics en un período de tiempo muy corto ) Me di cuenta que:
- La primera
navigate
invocación siempre funciona bien;
- La segunda y todas las demás invocaciones del
navigate
método se resuelven en IllegalArgumentException
.
Desde mi punto de vista, esta situación puede aparecer con mucha frecuencia. Como repetir código es una mala práctica y siempre es bueno tener un punto de influencia, pensé en la siguiente solución:
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.");
}
}
}
Y así, el código anterior cambia solo en una línea desde esto:
Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
a esto:
NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);
Incluso se hizo un poco más corto. El código se probó en el lugar exacto donde ocurrió el accidente. Ya no lo experimenté y usaré la misma solución para otras navegaciones para evitar el mismo error.
Cualquier idea es bienvenida!
¿Qué causa exactamente el accidente?
Recuerde que aquí trabajamos con el mismo gráfico de navegación, controlador de navegación y back-stack cuando usamos el método Navigation.findNavController
.
Siempre obtenemos el mismo controlador y gráfico aquí. Cuando navigate(R.id.my_next_destination)
se llama gráfico y el back-stack cambia casi instantáneamente mientras la interfaz de usuario aún no se actualiza. Simplemente no lo suficientemente rápido, pero eso está bien. Después de que el back-stack ha cambiado, el sistema de navegación recibe la segunda navigate(R.id.my_next_destination)
llamada. Dado que el back-stack ha cambiado, ahora operamos en relación con el fragmento superior de la pila. El fragmento superior es el fragmento al que navega utilizando R.id.my_next_destination
, pero no contiene más destinos con ID R.id.my_next_destination
. Por lo tanto, se obtiene IllegalArgumentException
debido a la identificación de la que el fragmento no sabe nada.
Este error exacto se puede encontrar en el NavController.java
método findDestination
.