Android Menu Item导航崩溃:二次退出触发IllegalStateException问题
问题描述
首次从LoginFragment跳转至MainFragment后,点击菜单中的logout按钮可正常返回LoginFragment;但第二次重复该操作时,触发如下异常:
java.lang.IllegalStateException: Fragment MainFragment{632a8a3} (33a94b4d-9e85-4315-ae99-51e5edc7ddc2 id=0x7f08015e) did not return a View from onCreateView() or this was called before onCreateView()
相关代码
LoginFragment代码
class LoginFragment : Fragment() { private lateinit var binding : FragmentLoginBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) auth = Firebase.auth database = Firebase.database.reference } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentLoginBinding.inflate(inflater,container,false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.apply { btnConfirm.setOnClickListener { Navigation.findNavController(requireView()).navigate(R.id.action_loginFragment_to_mainFragment) } } } }
MainFragment代码
class MainFragment : Fragment() { private lateinit var binding : FragmentMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentMainBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val toolbar = binding.toolbar (requireActivity() as AppCompatActivity).setSupportActionBar(toolbar) requireActivity().addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { if(menu.size()==0){ menuInflater.inflate(R.menu.main_menu,menu) } } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.logout ->{ Navigation.findNavController(requireView()).navigate(R.id.action_mainFragment_to_loginFragment) true } else -> false } } }) } }
导航配置文件
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_menu" app:startDestination="@id/splashFragment"> <fragment android:id="@+id/loginFragment" android:name="com.yasinsenel.yapacaklarm.view.fragment.LoginRegisterFragment" android:label="fragment_login" tools:layout="@layout/fragment_login" > <action android:id="@+id/action_loginFragment_to_mainFragment" app:destination="@id/mainFragment" app:enterAnim="@anim/nav_default_enter_anim" app:exitAnim="@anim/nav_default_exit_anim" /> </fragment> <fragment android:id="@+id/mainFragment" android:name="com.yasinsenel.yapacaklarm.view.fragment.MainFragment" android:label="fragment_main" tools:layout="@layout/fragment_main" > <action android:id="@+id/action_mainFragment_to_loginFragment" app:destination="@id/loginFragment" /> </navigation>
错误原因
- 导航栈冗余叠加:当前从
MainFragment到LoginFragment的导航action未配置popUpTo和popUpToInclusive属性,每次跳转都会在导航栈中新增LoginFragment实例,旧的MainFragment并未被弹出。第二次操作时,旧的MainFragment视图已销毁(onDestroyView执行),但它绑定的菜单提供者仍在Activity中生效,点击logout时会调用旧实例的requireView(),触发视图不存在的异常。 - MenuProvider未绑定生命周期:
MainFragment中添加的MenuProvider未与Fragment的视图生命周期绑定,也未在视图销毁时移除,导致Activity中存在多个无效的MenuProvider实例,旧实例响应菜单点击时会访问已销毁的视图。
解决方法
1. 修复导航栈配置,清理冗余Fragment
修改导航配置文件中mainFragment到loginFragment的action,添加栈清理属性:
<action android:id="@+id/action_mainFragment_to_loginFragment" app:destination="@id/loginFragment" app:popUpTo="@id/loginFragment" app:popUpToInclusive="true"/>
app:popUpTo="@id/loginFragment":跳转时弹出栈内直到loginFragment的所有Fragmentapp:popUpToInclusive="true":将loginFragment本身也弹出,确保栈内只有一个新的loginFragment实例,避免重复叠加
2. 绑定MenuProvider到Fragment生命周期
在MainFragment添加MenuProvider时,指定绑定到Fragment的视图生命周期,这样当视图销毁时,MenuProvider会自动移除:
requireActivity().addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { if(menu.size()==0){ menuInflater.inflate(R.menu.main_menu,menu) } } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.logout ->{ Navigation.findNavController(requireView()).navigate(R.id.action_mainFragment_to_loginFragment) true } else -> false } } }, viewLifecycleOwner) // 绑定到Fragment视图生命周期
或者手动在视图销毁时移除MenuProvider:
private lateinit var menuProvider: MenuProvider override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // ...其他代码 menuProvider = object : MenuProvider { // ...原有实现 } requireActivity().addMenuProvider(menuProvider) } override fun onDestroyView() { super.onDestroyView() requireActivity().removeMenuProvider(menuProvider) }
内容的提问来源于stack exchange,提问作者edoo000
相关产品推荐
相关产品推荐

