使用 LiveData 和 ViewModel 删除项目会导致重新发射
Posted
技术标签:
【中文标题】使用 LiveData 和 ViewModel 删除项目会导致重新发射【英文标题】:Remove item using LiveData and ViewModel causes re-emitting 【发布时间】:2021-01-21 08:32:47 【问题描述】:我有一个显示项目列表的片段,从视图模型观察(从 http 服务,它们没有保存在数据库中)。现在,我需要删除其中一项。我有一个删除结果实时数据,因此视图可以观察项目何时被删除。
片段
fun onViewCreated(view: View, savedInstanceState: Bundle?)
//...
viewModel.deleteItemLiveData.observe(viewLifecycleOwner)
when (it.status)
Result.Status.ERROR -> showDeletingError()
Result.Status.SUCCESS ->
itemsAdapter.remove(it.value)
commentsAdapter.notifyItemRemoved(it.value)
fun deleteItem(itemId: String, itemIndex: Int) = lifecycleScope.launch
viewModel.deleteItem(itemId, itemIndex)
视图模型
val deleteItemLiveData = MutableLiveData<Result<Int>>()
suspend fun deleteItem(itemId: String, itemIndex: Int) = withContext(Dispatchers.IO)
val result = service.deleteItem(itemId)
withContext(Dispatchers.Main)
if (result.success)
deleteItemLiveData.value = Result.success(itemIndex)
else
deleteItemLiveData.value = Result.error()
它工作正常,但是当我导航到另一个片段并再次返回时出现问题。 deleteItemLIveData
与最后一个 Result
再次发出,因此 Fragment 尝试再次从适配器中删除该项目,它崩溃了。
我该如何解决这个问题?
【问题讨论】:
为什么片段在加载时会检索到陈旧的项目列表?为什么它没有得到更新的列表? 我能做到。但在那种情况下,我会遇到同样的问题。如何查看删除结果,例如显示错误消息而不在每次恢复时重新显示? 这是一个架构问题,您正在尝试从适配器中删除已删除的项目,而不是收听对删除做出反应的更新的项目列表。 我理解你。但想象一下,我侦听更新的列表,而不是从适配器中删除该项目。在那种情况下,我坚持,如何查看观察删除结果,以防万一失败,在屏幕上显示错误? 我添加了一个答案,解释了我将如何处理这个问题。关于错误,不应将其与存储库中的数据相结合,在删除时向用户显示就足够了。 【参考方案1】:与其从适配器中删除单个项目,不如更新 LiveData 的原始源,因为视图会观察该列表。
项目存储库应处理删除,从 LiveData 中删除该项目,然后将更新传播到视图,然后传播到适配器。
回购可能看起来像这样......
fun deleteItem(item: Item): Result
val updated = items.value
updated.remove(item)
items.postValue(updated)
. . .
// propagate result of success/failure back to the view
fun observeItems() = items
在您的片段中,您将从单个 LiveData 源获得即时更新
fun onViewCreated(view: View, savedInstanceState: Bundle?)
viewModel.observeItems().observe(viewLifecycleOwner)
itemsAdapter.update(it) //use DiffUtil to update list or notifyDataSetChanged
显示错误应该是上下文相关的、祝酒词或一些视觉通知。
更新: 删除中的句柄错误可能看起来像这样,我想不到......
suspend fun deleteItem(itemId: String, itemIndex: Int): Result = withContext(Dispatchers.IO)
val result = service.deleteItem(itemId)
withContext(Dispatchers.Main)
if (result.success)
// push updated list to items
val updated = items.value
updated.remove(item)
items.postValue(updated)
Result.Success()
else
Result.error()
【讨论】:
删除时出现错误,视图如何通知? 我已经用作为概念返回的通用结果更新了我的答案,您可以将其映射到您的答案并从视图中观察操作,或者拥有您的视图观察到的 LiveData我找到了解决方案。我更改了我的代码,因此片段从onCreate
方法而不是onViewCreated
观察。我也换了主人。而不是viewLifecycleOwner
现在是this
。这样,在片段恢复时不会重新发出值,而是在创建或专门调用 viewModel.deleteItem
时重新发出值。
它现在工作正常。如果有人认为这是一个不好的解决方案,请告诉我。
【讨论】:
【参考方案3】:当您将LiveData
用于应该只发生一次的事件时,这是一个常见问题。 here 和 here 解释了几种解决方案。它们要么包装发出的数据,要么包装观察者。在这个包装器中,它们存储了一个标志,用于跟踪事件是否已被处理/发出。
【讨论】:
以上是关于使用 LiveData 和 ViewModel 删除项目会导致重新发射的主要内容,如果未能解决你的问题,请参考以下文章
JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )
使用 ViewModel 和 LiveData 多次改造执行 API