当我试图从一个片段导航到另一个片段时,我遇到了新的Android导航架构组件的问题,我得到了这个奇怪的错误:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
其他导航都很好,除了这个。
我使用Fragment的findNavController()函数来访问NavController。
任何帮助都将不胜感激。
当我试图从一个片段导航到另一个片段时,我遇到了新的Android导航架构组件的问题,我得到了这个奇怪的错误:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
其他导航都很好,除了这个。
我使用Fragment的findNavController()函数来访问NavController。
任何帮助都将不胜感激。
当前回答
在我的情况下,我通过验证所有导航操作都正确地管理到各自的图形中,并更新了设备后退按钮的代码来解决这个问题,如下所示:
onBackPressedDispatcher.addCallback(this /* lifecycle owner */, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// Back is pressed... Finishing the activity
if (navHostFragment.childFragmentManager.backStackEntryCount == 0) {
// First fragment is open, backstack is empty
finish()
} else {
navHostFragment.navController.popBackStack()
}
}
})
其他回答
为了避免这种崩溃,我的一个同事写了一个小库,它公开了一个SafeNavController,一个围绕NavController的包装器,并处理由于多个导航命令同时发生崩溃的情况。
这里有一篇关于整个问题和解决方案的短文。
你可以在这里找到图书馆。
我写了这个扩展
fun Fragment.navigateAction(action: NavDirections) {
val navController = this.findNavController()
if (navController.currentDestination?.getAction(action.actionId) == null) {
return
} else {
navController.navigate(action)
}
}
我在我的项目中也有同样的问题,首先我试图在视图上触发导航动作的点击,但经过一些实验后,我发现在真正缓慢的设备上,debounce应该是一个非常高的值,导致应用程序对快速设备的用户感到缓慢。
所以我为NavController提出了以下扩展,我认为它符合原始的API,并且易于使用:
fun NavController.safeNavigate(directions: NavDirections) {
try {
currentDestination?.getAction(directions.actionId) ?: return
navigate(directions.actionId, directions.arguments, null)
} catch (e : Exception) {
logError("Navigation error", e)
}
}
fun NavController.safeNavigate(directions: NavDirections, navOptions: NavOptions?) {
try {
currentDestination?.getAction(directions.actionId) ?: return
navigate(directions.actionId, directions.arguments, navOptions)
} catch (e : Exception) {
logError("Navigation error", e)
}
}
fun NavController.safeNavigate(directions: NavDirections, navigatorExtras: Navigator.Extras) {
try {
currentDestination?.getAction(directions.actionId) ?: return
navigate(directions.actionId, directions.arguments, null, navigatorExtras)
} catch (e : Exception) {
logError("Navigation error", e)
}
}
请注意,我正在使用SafeArgs和NavDirections。这些函数检查操作从当前目的地是否有效,并且仅在操作不为空时进行导航。如果Navigation库每次都返回正确的操作,那么try catch部分就不需要了,但我希望消除所有可能的崩溃。
似乎混合fragmentManager控件的backstack和Navigation Architecture控件的backstack也会导致这个问题。
例如,最初的CameraX基本示例使用fragmentManager后台导航,如下所示,它似乎没有正确地与导航交互:
// Handle back button press
view.findViewById<ImageButton>(R.id.back_button).setOnClickListener {
fragmentManager?.popBackStack()
}
如果你在从主片段(在本例中是相机片段)移动之前记录这个版本的“当前目的地”,然后当你返回主片段时再次记录它,你可以从日志中的id中看到id是不相同的。据猜测,导航在移动到片段时更新了它,而fragmntManager在移动回来时没有再次更新它。日志显示:
修改前:D/CameraXBasic: currentDest?: androidx.navigation.fragment.FragmentNavigator Destination@b713195美元 After: D/CameraXBasic: currentDest?: androidx.navigation.fragment.FragmentNavigator Destination@9807d8f美元
更新版的CameraX基本示例使用导航返回如下:
// Handle back button press
view.findViewById<ImageButton>(R.id.back_button).setOnClickListener {
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigateUp()
}
这可以正常工作,当返回到主片段时,日志显示相同的id。
修改前:D/CameraXBasic: currentDest?: androidx.navigation.fragment.FragmentNavigator Destination@b713195美元 After: D/CameraXBasic: currentDest?: androidx.navigation.fragment.FragmentNavigator Destination@b713195美元
我怀疑这个故事的寓意,至少在这个时候,是要非常小心地混合导航和fragmentManager导航。
还有另一种解决快速点击导航崩溃问题的方法:
fun NavController.doIfCurrentDestination(@IdRes destination: Int, action: NavController.()-> Unit){
if(this.currentDestination?.id == destination){action()}
}
然后像这样使用:
findNavController().doIfCurrentDestination(R.id.my_destination){ navigate(...) }
这种解决方案的好处是,您可以轻松地用您已经使用的任何签名包装任何现有的naagte()调用,而不需要进行一百万次重载