Fragment返回后数据持久化异常:新建详情页残留旧列表数据
问题:新建详情页残留旧列表数据
重复执行详情页添加列表项并完成的流程时,新建的Detail Fragment会残留之前的列表数据。比如之前详情页创建了3条列表项,新详情页添加数据时,会在已有3条数据的基础上新增内容。
Fragment代码
class WriteDetailFragment : Fragment() { ... private val viewModel: WriteDetailViewModel by viewModels { WriteDetailViewModelFactory( (requireActivity().application as WorkoutApplication).writeDetailRepo ) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentWriteDetailBinding.inflate(inflater, container, false) binding.apply { adapter = DetailAdapter() rv.adapter = adapter rv.itemAnimator = null // 添加组 addSet.setOnClickListener { viewModel.addSet() } // 完成 complete.setOnClickListener { findNavController().navigate(R.id.action_writeDetailFragment_to_addRoutineFragment) } } return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.items.collect { list -> adapter.submitList(list) } } } } }
ViewModel代码
class WriteDetailViewModel( private val repository: WriteDetailRepository ): ViewModel() { private var _items: MutableStateFlow<List<WorkoutSetInfo>> = MutableStateFlow(listOf()) val items = _items.asStateFlow() fun addSet() { viewModelScope.launch(Dispatchers.IO) { repository.add() _items.value = repository.getList() } } }
Repository代码
class WriteDetailRepository(val dao: WorkoutDao) { private var setInfoList = ArrayList<WorkoutSetInfo>() private lateinit var updatedList: List<WorkoutSetInfo> fun add() { val item = WorkoutSetInfo(set = setInfoList.size + 1) setInfoList.add(item) updatedList = setInfoList.toList() } fun getList() : List<WorkoutSetInfo> = updatedList }
问题根源与解决办法
问题根源
WriteDetailRepository是全局单例(从WorkoutApplication获取),其中的setInfoList是类成员变量,会在App生命周期内一直保留数据。每次新建Detail Fragment时,复用了同一个Repository实例,导致旧数据被保留。
解决办法
方案一:每次创建ViewModel时初始化新的Repository
修改ViewModelFactory,确保每次创建WriteDetailViewModel时生成新的WriteDetailRepository实例:
class WriteDetailViewModelFactory( private val dao: WorkoutDao // 从Application传入Dao,而非现成的Repository ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(WriteDetailViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return WriteDetailViewModel(WriteDetailRepository(dao)) as T } throw IllegalArgumentException("Unknown ViewModel class") } }
然后修改Fragment中获取ViewModel的代码:
private val viewModel: WriteDetailViewModel by viewModels { WriteDetailViewModelFactory( (requireActivity().application as WorkoutApplication).workoutDao ) }
方案二:给Repository添加重置方法
在Repository中添加清空列表的方法,在Fragment初始化时调用:
class WriteDetailRepository(val dao: WorkoutDao) { private var setInfoList = ArrayList<WorkoutSetInfo>() private lateinit var updatedList: List<WorkoutSetInfo> fun reset() { setInfoList.clear() updatedList = emptyList() } fun add() { val item = WorkoutSetInfo(set = setInfoList.size + 1) setInfoList.add(item) updatedList = setInfoList.toList() } fun getList() : List<WorkoutSetInfo> = updatedList }
在ViewModel中新增对应方法:
fun resetSets() { viewModelScope.launch(Dispatchers.IO) { repository.reset() _items.value = repository.getList() } }
最后在Fragment的onViewCreated中调用:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.resetSets() // 初始化时清空数据 // 其他原有代码... }
额外优化建议
- 避免在Repository中持有临时页面状态,这类状态应该由ViewModel管理,或者在页面销毁时主动清空。
- 使用
submitList时,确保每次传入新的List实例(比如通过toList()创建副本),保证Adapter能正确识别数据变化。
内容的提问来源于stack exchange,提问作者ybybyb
相关产品推荐
相关产品推荐

