TL; DR Kết thúc navigate
cuộc gọi của bạn bằng try-catch
(cách đơn giản) hoặc đảm bảo rằng sẽ chỉ có một cuộc gọi trong sốnavigate
trong khoảng thời gian ngắn. Vấn đề này có thể sẽ không biến mất. Sao chép đoạn mã lớn hơn trong ứng dụng của bạn và dùng thử.
Xin chào. Dựa trên một số câu trả lời hữu ích ở trên, tôi muốn chia sẻ giải pháp của mình có thể được mở rộng.
Đây là mã gây ra sự cố này trong ứng dụng của tôi:
@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);
}
Một cách để dễ dàng tái tạo lỗi là chạm bằng nhiều ngón tay vào danh sách các mục mà lần nhấp vào mỗi mục sẽ giải quyết trong điều hướng đến màn hình mới (về cơ bản giống như mọi người đã lưu ý - hai lần nhấp trở lên trong một khoảng thời gian rất ngắn ). Tôi nhận thấy rằng:
- Lời
navigate
kêu gọi đầu tiên luôn hoạt động tốt;
- Thứ hai và tất cả các lệnh gọi khác của
navigate
phương thức giải quyết trong IllegalArgumentException
.
Theo quan điểm của tôi, tình trạng này có thể xuất hiện rất thường xuyên. Vì việc lặp lại mã là một thực tế không tốt và luôn tốt nếu có một điểm ảnh hưởng nên tôi đã nghĩ đến giải pháp tiếp theo:
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.");
}
}
}
Và do đó, mã ở trên chỉ thay đổi trong một dòng từ sau:
Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
đến điều này:
NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);
Nó thậm chí còn trở nên ngắn hơn một chút. Mã đã được kiểm tra ở chính xác nơi xảy ra sự cố. Không trải nghiệm nó nữa và sẽ sử dụng giải pháp tương tự cho các điều hướng khác để tránh thêm sai lầm tương tự.
Mọi suy nghĩ đều được chào đón!
Chính xác nguyên nhân gây ra sự cố
Hãy nhớ rằng ở đây chúng tôi làm việc với cùng một biểu đồ điều hướng, bộ điều khiển điều hướng và ngăn xếp dự phòng khi chúng tôi sử dụng phương pháp Navigation.findNavController
.
Chúng tôi luôn nhận được cùng một bộ điều khiển và đồ thị ở đây. Khi nào navigate(R.id.my_next_destination)
được gọi là đồ thị và ngăn xếp thay đổi gần như ngay lập tức trong khi giao diện người dùng chưa được cập nhật. Chỉ là không đủ nhanh, nhưng điều đó là ổn. Sau khi back-stack đã thay đổi, hệ thống điều hướng sẽ nhận được navigate(R.id.my_next_destination)
cuộc gọi thứ hai . Vì back-stack đã thay đổi, giờ đây chúng tôi hoạt động liên quan đến phân đoạn trên cùng trong ngăn xếp. Phân đoạn trên cùng là phân đoạn bạn điều hướng đến bằng cách sử dụng R.id.my_next_destination
, nhưng nó không chứa bất kỳ đích nào tiếp theo có ID R.id.my_next_destination
. Vì vậy, bạn nhận được IllegalArgumentException
do ID mà phân đoạn không biết gì về.
Lỗi chính xác này có thể được tìm thấy trong NavController.java
phương pháp findDestination
.