将 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