TL; DR Оберните ваши navigate
вызовы try-catch
(простой способ) или убедитесь, что за короткий промежуток времени будет только один вызов navigate
. Эта проблема, вероятно, не исчезнет. Скопируйте фрагмент кода большего размера в свое приложение и попробуйте.
Hello. Основываясь на нескольких полезных ответах выше, я хотел бы поделиться своим решением, которое можно расширить.
Вот код, вызвавший этот сбой в моем приложении:
@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);
}
Способ легко воспроизвести ошибку - это нажать несколькими пальцами на список элементов, где клик на каждом элементе разрешается при переходе на новый экран (в основном то же, что отмечали люди - два или более клика за очень короткий промежуток времени). промежуток времени). Я заметил, что:
- Первый
navigate
вызов всегда работает нормально;
- Второй и все другие вызовы метода
navigate
разрешаются в IllegalArgumentException
.
С моей точки зрения, такая ситуация может появляться очень часто. Поскольку повторение кода - плохая практика, и всегда полезно иметь одну точку влияния, я подумал о следующем решении:
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.");
}
}
}
И, следовательно, приведенный выше код изменяется только в одну строку из этого:
Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
на это:
NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);
Он даже стал немного короче. Код был протестирован в том месте, где произошел сбой. Больше не испытывал, и будет использовать то же решение для других навигаций, чтобы избежать той же ошибки в дальнейшем.
Любые мысли приветствуются!
Что именно вызывает сбой
Помните, что здесь мы работаем с одним и тем же графом навигации, контроллером навигации и задним стеком, когда используем метод Navigation.findNavController
.
Мы всегда получаем один и тот же контроллер и график здесь. Когда вызывается navigate(R.id.my_next_destination)
, график и бэк-стек меняются почти мгновенно , пока пользовательский интерфейс еще не обновлен. Просто не достаточно быстро, но это нормально. После изменения бэк-стека навигационная система получает второй вызов navigate(R.id.my_next_destination)
. Поскольку бэк-стек изменился, мы теперь работаем относительно верхнего фрагмента в стеке. Верхний фрагмент - это фрагмент, к которому вы переходите, используя R.id.my_next_destination
, но он не содержит следующих дальнейших пунктов назначения с идентификатором R.id.my_next_destination
. Таким образом, вы получаете IllegalArgumentException
из-за идентификатора, о котором фрагмент ничего не знает.
Точную ошибку можно найти в NavController.java
методе findDestination
.