使用Navigation Component时ViewModel中LiveData数据不持久化的原因是什么
问题原因
- 导航逻辑错误:从数据选择页返回列表页时,使用了
navigate()方法跳转到新的WriteRoutineFragment实例,而非调用popBackStack()回退到已存在的实例。每次调用navigate()指向同个导航目的地都会生成全新的Fragment实例,默认绑定到Fragment作用域的ViewModel也会同步重新初始化,因此每次添加数据时都是在新的空列表基础上操作,size自然始终为1。 - 参数传递逻辑不匹配:当前参数是通过导航跳转传递的,仅在新创建Fragment实例时能拿到一次参数,回退场景下无法触发参数接收逻辑。
解决方案
方案1:使用导航返回结果回调(推荐)
该方案复用原有Fragment实例,无需调整ViewModel作用域,符合单任务栈的导航设计规范:
- 修改
WriteRoutineFragment逻辑,移除原有的参数接收代码,注册返回结果监听:
class WriteRoutineFragment : Fragment() { private var _binding : FragmentWriteRoutineBinding? = null private val binding get() = _binding!! private lateinit var adapter : RoutineAdapter private val vm : WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { _binding = FragmentWriteRoutineBinding.inflate(inflater, container, false) adapter = RoutineAdapter(::addDetail, ::deleteDetail) binding.rv.adapter = this.adapter return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 注册选择页返回的结果监听 childFragmentManager.setFragmentResultListener("workout_select_key", viewLifecycleOwner) { _, bundle -> val workout = bundle.getString("selected_workout") workout?.let { vm.addRoutine(it) Toast.makeText(context, it, Toast.LENGTH_SHORT).show() } } // 跳转选择页的按钮逻辑保留原有跳转即可 binding.toSelectPageBtn.setOnClickListener { findNavController().navigate(R.id.action_writeRoutineFragment_to_workoutListTabFragment) } vm.items.observe(viewLifecycleOwner) { updatedItems -> adapter.setItems(updatedItems) } } override fun onDestroyView() { super.onDestroyView() _binding = null } }
- 修改数据选择页
WorkoutListTabFragment的选中逻辑,选完数据后回退并携带结果:
// 选中数据后执行的逻辑 val selectedWorkout = // 你选中的内容 setFragmentResult("workout_select_key", bundleOf("selected_workout" to selectedWorkout)) // 直接回退到上一页,不要调用navigate到writeRoutine目的地 findNavController().popBackStack()
方案2:绑定ViewModel到导航图作用域
如果业务需要保留多实例Fragment的数据,可以将ViewModel的作用域绑定到上层导航图,同个导航图下的所有Fragment都会共享同一个ViewModel实例:
- 先给你的导航根节点设置id,例如
nav_graph_app - 修改ViewModel初始化代码:
// 替换原来的by viewModels() private val vm : WriteRoutineViewModel by navGraphViewModels(R.id.nav_graph_app) { WriteRoutineViewModelFactory() }
补充优化
ViewModel的添加方法可以优化空安全处理:
fun addRoutine(workout: String) { val item = RoutineModel(workout, "TEST") item.setSubItemList(detailItem) val currentList = _items.value ?: arrayListOf() currentList.add(item) _items.value = currentList }
内容的提问来源于stack exchange,提问作者ybybyb
相关产品推荐
相关产品推荐

