TL; DR Owijaj swoje navigate
połączenia try-catch
(w prosty sposób) lub upewnij się, że navigate
w krótkim czasie będzie tylko jedno połączenie . Ten problem prawdopodobnie nie zniknie. Skopiuj większy fragment kodu do swojej aplikacji i wypróbuj.
Dzień dobry. W oparciu o kilka przydatnych odpowiedzi powyżej, chciałbym podzielić się moim rozwiązaniem, które można rozszerzyć.
Oto kod, który spowodował awarię w mojej aplikacji:
@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);
}
Sposobem na łatwe odtworzenie błędu jest dotknięcie wieloma palcami listy elementów, gdzie kliknięcie każdego elementu powoduje przejście do nowego ekranu (w zasadzie to samo, co ludzie zauważyli - dwa lub więcej kliknięć w bardzo krótkim czasie ). Zauważyłem to:
- Pierwsze
navigate
wywołanie zawsze działa dobrze;
- Drugie i wszystkie inne wywołania
navigate
metody są rozwiązywane w IllegalArgumentException
.
Z mojego punktu widzenia taka sytuacja może pojawiać się bardzo często. Ponieważ powtarzanie kodu jest złą praktyką i zawsze dobrze jest mieć jeden punkt wpływu, pomyślałem o następnym rozwiązaniu:
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.");
}
}
}
I tak powyższy kod zmienia się tylko w jednej linii z tego:
Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
do tego:
NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);
Stało się nawet trochę krótsze. Kod został przetestowany w dokładnym miejscu, w którym nastąpiła awaria. Już tego nie doświadczyłem i użyję tego samego rozwiązania w innych nawigacjach, aby dalej uniknąć tego samego błędu.
Wszelkie myśli są mile widziane!
Co dokładnie powoduje awarię
Pamiętaj, że tutaj pracujemy z tym samym wykresem nawigacyjnym, kontrolerem nawigacji i stosem wstecznym, gdy używamy metody Navigation.findNavController
.
Tutaj zawsze otrzymujemy ten sam kontroler i wykres. Kiedy navigate(R.id.my_next_destination)
nazywa się wykres i zmiany stosu wstecznego prawie natychmiast, gdy interfejs użytkownika nie jest jeszcze aktualizowany. Po prostu nie wystarczająco szybko, ale to jest w porządku. Po zmianie stosu system nawigacji odbiera drugie navigate(R.id.my_next_destination)
wywołanie. Ponieważ stos się zmienił, działamy teraz względem górnego fragmentu stosu. Górny fragment to fragment, do którego nawigujesz R.id.my_next_destination
, ale nie zawiera żadnych dalszych celów z identyfikatorem R.id.my_next_destination
. W ten sposób otrzymujesz IllegalArgumentException
ze względu na identyfikator, o którym fragment nic nie wie.
Dokładny błąd można znaleźć w NavController.java
metodzie findDestination
.