将 MediatorLiveData 迁移到 SharedFlow
Migrating a MediatorLiveData to SharedFlow
我有一个使用三个 LiveData 源的 MediatorLiveData。当它们中的任何一个发出一个新值并且我至少有一个时,我使用这三个值来生成 UI.
的输出
其中两个来源是关于如何排序和过滤列表的用户设置,第三个来源是列表数据,从 Room 数据库流中提取。
看起来像这样:
val thingsLiveData: LiveData<List<Thing>> = object: MediatorLiveData<List<Thing>>() {
var isSettingA: Boolean = true
var settingB: MySortingEnum = MySortingEnum.Alphabetical
var data: List<Thing>? = null
init {
addSource(myRepo.thingsFlow.asLiveData()) {
data = it
dataToValue()
}
addSource(settingALiveData) {
isSettingA= it
dataToValue()
}
addSource(settingBLiveData) {
settingB= it
dataToValue()
}
}
private fun dataToValue() {
data?.let { data ->
viewModelScope.launch {
val uiList = withContext(Dispatchers.Default) {
produceUiList(data, isSettingA, settingB)
}
value = listItems
}
}
}
}
我正在寻找一种将其转换为 SharedFlow 的简洁方法,最好没有任何 @ExperimentalCoroutinesApi
。我遇到的唯一 SharedFlow 构建器函数是 callbackFlow
,它不适用。在大多数情况下,您打算使用 flow { ... }.asSharedFlow(...)
吗?如果是,那么这里会是什么样子?
LiveData这两个设置我也打算迁移到flows
源流可以使用 combine()
组合,这会创建一个冷流,当收集时,将从其源流开始收集,它可能是热的也可能是冷的。
我本来在想,我一定是漏掉了什么,应该有什么方法可以直接将hot Flows合并成一个combined hot Flow。但我意识到,操作员应该只 return 冷流并让您将其转换回热流(如果您需要的话)是有道理的。
在很多情况下,例如我的情况,将其冷藏就可以了。我只从我的 UI 中的一个地方收集这个流,所以它只在收集时才开始合并源并不重要。源热流不关心当前是否有东西正在收集它们......它们只是不顾一切地继续发射。
如果我从多个地方或多次收集此流,那么在组合流上使用 shareIn
使其变热可能是有意义的,这将避免合并源的冗余工作。潜在的缺点是即使没有收集任何东西,它也会合并这些资源,这会浪费工作。
val thingsFlow: Flow<List<Thing>> = combine(
myRepo.thingsFlow,
settingALiveData.asFlow(),
settingBLiveData.asFlow()
) { data, isSettingA, settingB -> produceUiList(data, isSettingA, settingB) }
// where produceUiList is now a suspend function that wraps
// blocking code using withContext
我有一个使用三个 LiveData 源的 MediatorLiveData。当它们中的任何一个发出一个新值并且我至少有一个时,我使用这三个值来生成 UI.
的输出其中两个来源是关于如何排序和过滤列表的用户设置,第三个来源是列表数据,从 Room 数据库流中提取。
看起来像这样:
val thingsLiveData: LiveData<List<Thing>> = object: MediatorLiveData<List<Thing>>() {
var isSettingA: Boolean = true
var settingB: MySortingEnum = MySortingEnum.Alphabetical
var data: List<Thing>? = null
init {
addSource(myRepo.thingsFlow.asLiveData()) {
data = it
dataToValue()
}
addSource(settingALiveData) {
isSettingA= it
dataToValue()
}
addSource(settingBLiveData) {
settingB= it
dataToValue()
}
}
private fun dataToValue() {
data?.let { data ->
viewModelScope.launch {
val uiList = withContext(Dispatchers.Default) {
produceUiList(data, isSettingA, settingB)
}
value = listItems
}
}
}
}
我正在寻找一种将其转换为 SharedFlow 的简洁方法,最好没有任何 @ExperimentalCoroutinesApi
。我遇到的唯一 SharedFlow 构建器函数是 callbackFlow
,它不适用。在大多数情况下,您打算使用 flow { ... }.asSharedFlow(...)
吗?如果是,那么这里会是什么样子?
LiveData这两个设置我也打算迁移到flows
源流可以使用 combine()
组合,这会创建一个冷流,当收集时,将从其源流开始收集,它可能是热的也可能是冷的。
我本来在想,我一定是漏掉了什么,应该有什么方法可以直接将hot Flows合并成一个combined hot Flow。但我意识到,操作员应该只 return 冷流并让您将其转换回热流(如果您需要的话)是有道理的。
在很多情况下,例如我的情况,将其冷藏就可以了。我只从我的 UI 中的一个地方收集这个流,所以它只在收集时才开始合并源并不重要。源热流不关心当前是否有东西正在收集它们......它们只是不顾一切地继续发射。
如果我从多个地方或多次收集此流,那么在组合流上使用 shareIn
使其变热可能是有意义的,这将避免合并源的冗余工作。潜在的缺点是即使没有收集任何东西,它也会合并这些资源,这会浪费工作。
val thingsFlow: Flow<List<Thing>> = combine(
myRepo.thingsFlow,
settingALiveData.asFlow(),
settingBLiveData.asFlow()
) { data, isSettingA, settingB -> produceUiList(data, isSettingA, settingB) }
// where produceUiList is now a suspend function that wraps
// blocking code using withContext