如何在使用实时数据时链接Android中的转换?

Nav*_*eed 11 android kotlin android-livedata android-architecture-components

鉴于以下设置:

我有2个存储库:存储库A存储库B它们都返回实时数据.

我有一个使用这两个存储库的ViewModel.

我想从Repository A中提取一些内容,并根据结果我想从Repository B中获取一些东西,然后在返回UI之前转换结果.

为此我一直在看LiveData Transformation类.这些例子显示了结果的单一转换,但是我想要链接两个转换的行.我怎么能做到这一点?

我尝试过像这样设置但在第二个转换块上出现类型不匹配:

  internal val launchStatus: LiveData<String> = Transformations
        .map(respositoryA.getData(), { data ->
            if (data.isValid){
                "stringA"
            } else {
                //This gives a type mismatch for the entire block
                Transformations.map(repositoryB.getData(), {
                    result -> result.toString()
                })
            }
        })
Run Code Online (Sandbox Code Playgroud)

(另请告诉我是否有替代/推荐的方法来抓取链接这些呼叫的东西,即从A抓取一些东西然后根据A的结果从B中抓取一些东西,依此类推)

Chr*_*her 7

你的 lambda 有时返回String "stringA",有时返回LiveData<String>给定的:

Transformations.map(repositoryB.getData(), {
    result -> result.toString()
})
Run Code Online (Sandbox Code Playgroud)

这意味着你的 lambda 没有意义——它在不同的分支中返回不同的东西。

正如其他人所提到的,您可以编写自己的MediatorLiveData而不是使用Transformations. 但是,我认为执行以下操作更容易:

internal val launchStatus: LiveData<String> = Transformations
    .switchMap(respositoryA.getData(), { data ->
        if (data.isValid) {
            MutableLiveData().apply { setValue("stringA") }
        } else {
            Transformations.map(repositoryB.getData(), {
                result -> result.toString()
            })
        }
    })
Run Code Online (Sandbox Code Playgroud)

我所做的只是让第一个代码分支也返回 a LiveData<String>,所以现在您的 lambda 有意义 - 它是(String) -> LiveData<String>. 我不得不进行另一项更改:使用switchMap而不是map. 这是因为map需要一个 lambda (X) -> Y,但switchMap需要一个 lambda (X) -> LiveData<Y>


Nav*_*eed 5

我使用MediatorLiveData解决了此问题。

MediatorLiveData可以观察其他LiveData对象并对它们做出反应。

而不是观察任何一个存储库。我在类中创建了myData(MediatorLiveData的实例),ViewModel并让我的视图观察了该对象。然后,我将存储库A添加为初始源,并观察到它,并且仅在A的结果需要它时才添加存储库B。这使我可以保持与每个存储库的实时数据相关联的转换,并且仍然以正确的顺序处理每个结果。参见以下实现:

internal val myData: MediatorLiveData<String> = MediatorLiveData()

private val repoA: LiveData<String> = Transformations.map(
        respositoryA.getData(), { data ->
    if (data.isValid) "stringA" else ""

})

private val repoB: LiveData<String> = Transformations.map(
        repositoryB.getData(), { data -> "stringB" 
})

fun start() {
    myData.addSource(repoA, {
        if (it == "stringA") {
            myData.value = it
        } else {
            myData.addSource(repoB, {
                myData.value = it
            })
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

注意:该解决方案并不涵盖可能多次添加repoB的情况,但应足够简单以进行处理。