TL; DR Encerre suas navigate
chamadas com try-catch
(maneira simples) ou verifique se haverá apenas uma chamada navigate
em 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
navigate
invocação sempre funciona bem;
- A segunda e todas as outras invocações do
navigate
mé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 IllegalArgumentException
o ID que o fragmento não conhece.
Este erro exata pode ser encontrada no NavController.java
método findDestination
.