如何转换 3 个或更多 LiveData。在 Android 中合并为一个 Flow

How to convert 3 or more LiveData. into a single Flow in Android

视图模型有来自 3 个不同 api 调用的 3 个实时数据。现在,我想在回收站视图的列表中将所有数据一起显示为不同的卡片。

最好的方法是什么?

目前我正在使用 MediatorLiveData,但现在我需要进行 4 次不同的 api 调用,这将生成 4 个实时数据对象。因此,我正在尝试使用 kotlin flow 来解决这个问题。我怎样才能结合起来。来自不同用例的 4 个不同的实时数据进入单个流对象,然后将其收集在片段中?

以下是方法,我尝试使用组合方法组合 3 个实时数据:

private val flow1 = pojo1UseCase.data.asFlow()
private val flow2 = pojo2UseCase.data.asFlow()
private val flow3 = pojo3UseCase.data.asFlow()


suspend fun combine() {
    flow1.combine(flow2) { f1, f2 ->
        listOf(f1.right(), f2.right())
    }.combine(flow3) { f1, f3 ->
        listOf(f1, f3.right())
    }.collect { list ->
        list.forEach {
            ba = it as? Pojo1
            oc = it as? Pojo2
            sd = it as? Pojo3
        }
    }
}

我在这里调用 collect 只是为了测试目的,但我打算在片段中调用 collect 方法。 有人可以分享做类似事情的更好方法吗?

假设这很简单,并且您只希望每个 LiveData 有一个值,combine 应该没问题,但您可以使用带有三个参数的变体。

此外,由于它只是另一个冷流,因此可以简单地属性。冷流将由 LiveDatas 支持,因此如果在收到结果并收集流后出现某些情况,检索到的值不会丢失。只有在收集时才需要挂起功能。

val combined: Flow<List<Any>> = 
    combine(flow1, flow2, flow3) { a, b, c -> listOf(a, b, c) }

但我会推荐一个 class 来固定你的物体,这样你就不必在它们出来时施放它们。

data class MyResult(pojo1: Pojo1, pojo2: Pojo2, pojo3: Pojo3)

val combined: Flow<MyResult> = combine(flow1, flow2, flow3, ::MyResult)

因为没有什么可以自动取消这些流,如果你做一个收集它们的暂停功能,我认为你需要使用 first() 否则它会无限期暂停。

suspend fun combine() = with(combined.first()) {
    ba = pojo1
    oc = pojo2
    sd = pojo3
}

或者您可以使用 take(1) 定义您的流程,使其在第一个结果出现时自动完成。

val combined: Flow<MyResult> = combine(flow1, flow2, flow3, ::MyResult).take(1)

根据评论编辑:

如果你想无限期地收集它们,你可以在内部开始,或者你可以创建一个函数,你将调用一次来开始收集。你不应该使用挂起函数,因为无论哪个协程调用它都会永远卡在 collect() 调用上。它应该只是一个常规函数,它启动自己的协程来收集它。

init { 
    combined.onEach {
        with(it) {
            ba = pojo1
            oc = pojo2
            sd = pojo3
        }
    }.launchIn(viewModelScope)
}

// or:

fun startCombining() {
    combined.onEach {
        with(it) {
            ba = pojo1
            oc = pojo2
            sd = pojo3
        }
    }.launchIn(viewModelScope)
}