TL; DR Encerre suas navigatechamadas com try-catch(maneira simples) ou verifique se haverá apenas uma chamada navigateem um curto período de tempo. Esse problema provavelmente não desaparecerá. Copie um snippet de código maior no seu aplicativo e experimente.
Olá. Com base em algumas respostas úteis acima, gostaria de compartilhar minha solução que pode ser estendida.
Aqui está o código que causou essa falha no meu aplicativo:
@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);
}
Uma maneira de reproduzir facilmente o bug é tocar com vários dedos na lista de itens em que o clique em cada item é resolvido na navegação para a nova tela (basicamente o mesmo que as pessoas notaram - dois ou mais cliques em um período muito curto de tempo ) Eu percebi isso:
- A primeira
navigateinvocação sempre funciona bem;
- A segunda e todas as outras invocações do
navigatemétodo são resolvidas IllegalArgumentException.
Do meu ponto de vista, essa situação pode aparecer com muita frequência. Como a repetição de código é uma prática ruim e sempre é bom ter um ponto de influência, pensei na próxima solução:
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.");
}
}
}
E, portanto, o código acima muda apenas em uma linha a partir disso:
Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
para isso:
NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);
Até ficou um pouco mais curto. O código foi testado no local exato em que a falha ocorreu. Não o experimentou mais e usará a mesma solução para outras navegações para evitar ainda mais o mesmo erro.
Quaisquer pensamentos são bem-vindos!
O que exatamente causa o acidente
Lembre-se que aqui trabalhamos com o mesmo gráfico de navegação, controlador de navegação e volta-stack quando usamos método Navigation.findNavController.
Sempre temos o mesmo controlador e gráfico aqui. Quando navigate(R.id.my_next_destination)é chamado gráfico e a pilha traseira muda quase instantaneamente enquanto a interface do usuário ainda não está atualizada. Apenas não é rápido o suficiente, mas tudo bem. Após a troca da pilha traseira, o sistema de navegação recebe a segunda navigate(R.id.my_next_destination)chamada. Como a pilha traseira mudou, agora operamos em relação ao fragmento superior da pilha. O fragmento superior é o fragmento para o qual você navega usando R.id.my_next_destination, mas não contém outros destinos com ID R.id.my_next_destination. Assim, você obtém IllegalArgumentExceptiono ID que o fragmento não conhece.
Este erro exata pode ser encontrada no NavController.javamétodo findDestination.