TL; DR Schließen Sie Ihre navigate
Anrufe mit try-catch
(auf einfache Weise) ab oder stellen Sie sicher, dass navigate
in kurzer Zeit nur ein Anruf eingeht. Dieses Problem wird wahrscheinlich nicht verschwinden. Kopieren Sie ein größeres Code-Snippet in Ihre App und probieren Sie es aus.
Hallo. Basierend auf einigen nützlichen Antworten möchte ich meine Lösung teilen, die erweitert werden kann.
Hier ist der Code, der diesen Absturz in meiner Anwendung verursacht hat:
@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);
}
Eine Möglichkeit, den Fehler einfach zu reproduzieren, besteht darin, mit mehreren Fingern auf die Liste der Elemente zu tippen, wobei das Klicken auf jedes Element in der Navigation zum neuen Bildschirm aufgelöst wird (im Grunde das Gleiche wie die angegebenen Personen - zwei oder mehr Klicks in sehr kurzer Zeit ). Ich habe bemerkt, dass:
- Der erste
navigate
Aufruf funktioniert immer einwandfrei.
- Der zweite und alle anderen Aufrufe der
navigate
Methode werden in aufgelöst IllegalArgumentException
.
Aus meiner Sicht kann diese Situation sehr oft auftreten. Da das Wiederholen von Code eine schlechte Praxis ist und es immer gut ist, einen Einflusspunkt zu haben, dachte ich an die nächste Lösung:
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.");
}
}
}}
Und so ändert sich der obige Code nur in einer Zeile davon:
Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
dazu:
NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);
Es wurde sogar etwas kürzer. Der Code wurde genau an der Stelle getestet, an der der Absturz aufgetreten ist. Ich habe es nicht mehr erlebt und werde dieselbe Lösung für andere Navigationen verwenden, um denselben Fehler weiter zu vermeiden.
Irgendwelche Gedanken sind willkommen!
Was genau den Absturz verursacht
Denken Sie daran, dass wir hier mit demselben Navigationsdiagramm, Navigationscontroller und Backstack arbeiten, wenn wir die Methode verwenden Navigation.findNavController
.
Wir bekommen hier immer den gleichen Controller und die gleiche Grafik. Wann navigate(R.id.my_next_destination)
als Graph bezeichnet wird, ändert sich der Backstack fast sofort, während die Benutzeroberfläche noch nicht aktualisiert wurde. Nur nicht schnell genug, aber das ist ok. Nachdem sich der Backstack geändert hat, erhält das Navigationssystem den zweiten navigate(R.id.my_next_destination)
Anruf. Da sich der Backstack geändert hat, arbeiten wir jetzt relativ zum obersten Fragment im Stack. Das oberste Fragment ist das Fragment, zu dem Sie mit navigieren R.id.my_next_destination
, es enthält jedoch keine weiteren Ziele mit ID R.id.my_next_destination
. So erhalten Sie IllegalArgumentException
aufgrund der ID, von der das Fragment nichts weiß.
Dieser genaue Fehler kann in der NavController.java
Methode gefunden werden findDestination
.