如何将多个实时数据收集为一个流程?

问题描述 投票:0回答:1

所以我有一个具体的问题。

我们已经开始重构遗留代码,此时,我们需要采用遗留类。

因此遗留类会执行一些网络调用,并根据响应设置一些 liveData 对象。

在该功能中,它将被重构,而不是 liveData ,它将在挂起乐趣中执行所有操作并返回相应的数据,但现在我们无法使用这种方法。

所以我正在考虑某种混合解决方案。

我想在一个流程中收集所有这些实时数据,这将是一个用例。理论上只会发生一次发射,因为只有一个 liveData 会获得值。

在 viewModel 中,我订阅它并执行所有操作。第一次收集后我想销毁这个收集工作。也许我可以在流程中使用 takeWhile() 运算符。

好不好?会不会造成内存泄漏? takeWhile 将会

这就是现在的样子:

class UseCase(){
    operator fun invoke(a: LiveData<String>, b: LiveData<String>, c: LiveData<Int>) =
            flow<Result<String>> {
                a.asFlow().collect{
                    emit(Resource.Error)
                }

                b.asFlow().collect{
                    emit(Resource.Error)
                }

                c.asFlow().collect{
                    emit(Resource.Success)
                }
            }
}

class TestViewModel(): ViewModel(){

    fun getData(){
        var shouldCollect = true

        viewModelScope.launch {
            UseCase().invoke(MutableLiveData(""),MutableLiveData(""),MutableLiveData(1)).takeWhile { shouldCollect }.collect{
                shouldCollect = false
            }
        }
    }
}

我知道这不是一个好方法,但是一旦依赖类被重构,它就会被替换。

所以现在我尝试了一下,但行不通。只有列表中的第一个 liveData 会更新流程,因此如果第二个 liveData 获取值,它会由于某种原因不会发出它。我尝试合并/压缩/组合它们

android kotlin android-livedata
1个回答
0
投票

我不完全确定你想用它做什么,但你可以

merge
将同一类型的多个流合并到一个流中:

operator fun invoke(
    a: LiveData<String>,
    b: LiveData<String>,
    c: LiveData<Int>,
): Flow<Result<String>> = merge(
    a.asFlow().map { Resource.Error },
    b.asFlow().map { Resource.Error },
    c.asFlow().map { Resource.Success },
)

这将发出至少 3 个值,但不保证发出元素的顺序。

如果您想将流的值

combine
改为单个值,可以这样做:

operator fun invoke(
    a: LiveData<String>,
    b: LiveData<String>,
    c: LiveData<Int>,
): Flow<Result<String>> = combine(
    a.asFlow(),
    b.asFlow(),
    c.asFlow(),
) { valueA, valueB, valueC ->
    if (...) {
        Resource.Success
    } else {
        Resource.Error
    }
}

我也不确定你想在视图模型中实现什么目标。如果底层 LiveData 不再改变,那么仍然收集流量也没有什么坏处;无论如何,什么也不会发生。如果您只对第一个值感兴趣,并且想忽略之后的任何更改,您可以使用与您的方法类似的方法,但我不确定这在您的场景中有多强大:每次调用

getData()
时重新创建流,您可以有效地获取 LiveDatas 的当前值,而不是开始时的值。

但话又说回来,您甚至不应该在视图模型中收集流。总体思路是在尽可能最低的级别(在数据层中)生成流,并在 UI 中尽可能最新的时刻收集流。介于两者之间的所有内容,包括您的视图模型,都应该只转换流(如上面的

merge

combine
)。视图模型作为收集之前的最后一行,应该将所有流转换为 ui 状态对象流,并使其成为 
StateFlow
,其中:

stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = /* some initial value to use until the flows produce their first value */, )
如果您为 UI 使用 compose 并使用 

collectAsStateWithLifecycle

 收集结果流,则效果特别好。

© www.soinside.com 2019 - 2024. All rights reserved.