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

How to chain transformations in Android when using live data?

给定以下设置:

我有 2 个存储库:存储库 A存储库 B 它们都是 return 实时数据。

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

我想从存储库 A 中提取一些内容,根据结果,我想从存储库 B 中提取一些内容,然后在 returning 之前将结果转换为 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()
                })
            }
        })

(另外请让我知道是否有一种 alternate/recommended 方法来获取链接这些调用的内容,即从 A 获取内容,然后根据 A 的结果从 B 获取内容等等)

我会尝试使用 switchMap 而不是 map:

Similar to map(), applies a function to the value stored in the LiveData object and unwraps and dispatches the result downstream. The function passed to switchMap() must return a LiveData object.

您可以使用 switchmap 转换数据。这是一个示例 documentation.

我用MediatorLiveData解决了这个问题。

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

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

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
            })
        }
    })
}

注意:解决方案不包括repoB可能被多次添加的情况,但应该足够简单处理。

您的 lambda 有时 return 是 String "stringA",有时 return 是 LiveData<String> 给出的:

Transformations.map(repositoryB.getData(), {
    result -> result.toString()
})

这意味着您的 lambda 没有意义 - 它 return 在不同的分支中有不同的东西。

正如其他人所提到的,您 可以 编写自己的 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()
            })
        }
    })

我所做的就是使第一个代码分支也 return 成为 LiveData<String>,所以现在您的 lambda 变得有意义了 - 它是 (String) -> LiveData<String>。我不得不进行另一项更改:使用 switchMap 而不是 map。这是因为 map 采用 lambda (X) -> Y,但 switchMap 采用 lambda (X) -> LiveData<Y>.

您可以嵌套转换。

val finalLiveData = Transformations.switchMap(liveData1){
      val search = it
      Transformations.switchMap(liveData2) {
         db(context).dao().all(search, it)
     }
}