TL;DR 导航控制器的变化比UI更快,您在两个不同的状态下向导航控制器发送了两个相同的navigate(R.id.destn_id)
。
为了解决这个问题,可以将navigate
调用包装在try-catch
中(简单方法),或者确保在短时间内只有一个navigate
调用。这个问题可能不会消失。在应用程序中复制更大的代码片段并尝试。
你好。基于上面几个有用的回答,我想分享我的解决方案,可以进行扩展。
以下是在我的应用程序中导致崩溃的代码:
@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, null);
}
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)
时,图形和后退栈几乎立即更改,而UI尚未更新。只是不够快,但没关系。后退栈更改后,导航系统接收到第二个navigate(R.id.my_next_destination)
调用。由于后退栈已更改,因此我们现在相对于堆栈中的顶部片段进行操作。顶部片段是您通过使用R.id.my_next_destination
导航到的片段,但它不包含具有IDR.id.my_next_destination
的下一个任何目的地。因此,由于该片段对ID一无所知,您会收到IllegalArgumentException
。
此确切错误可以在NavController.java
方法findDestination
中找到。