我正在这样做codelab关于 Kotlin 协程。从第 6 步到第 7 步,我应该更新此方法:
fun getPlantsWithGrowZone(growZone: GrowZone) = liveData {
val plantsGrowZoneLiveData = plantDao.getPlantsWithGrowZoneNumber(growZone.number) // db
val customSortOrder = plantsListSortOrderCache.getOrAwait() // network
emitSource(plantsGrowZoneLiveData.map { plantList ->
plantList.applySort(customSortOrder)
})
}
对于这个:
fun getPlantsWithGrowZone(growZone: GrowZone) =
plantDao.getPlantsWithGrowZoneNumber(growZone.number) // db
.switchMap { plantList ->
liveData {
val customSortOrder = plantsListSortOrderCache.getOrAwait() //network
emit(plantList.applyMainSafeSort(customSortOrder))
}
}
Codelab 解释了第二个区别,即在 map 中进行网络调用是安全的,因为它已被缓存。但我不明白为什么?
最佳答案
对功能进行此更改似乎有两个目标,但第二个目标没有明确说明。
- 在与主线程不同的线程上进行排序,因此不会降低 UI 性能。
- 每次数据库更新植物列表时,都会从网络重新获取排序方法,以防排序方法发生更改。在前面的代码中,排序方法仅预先获取一次,然后在每次数据库中更新植物列表时重复使用,因此排序顺序可能会过时。
实现目标 1:
第 1 步是创建排序函数的一个版本,它是一个不在主线程上运行的挂起函数。 withContext
在挂起函数中使用,将排序移动到不同的线程(通过使用不同的调度程序指定)。
第2步是在映射数据时使用这个挂起函数。 liveData.map 函数的 lambda 无法调用挂起函数,因为它不是在协程中运行。它直接在主线程上运行。因此,我们必须改变绘制数据的方法。
使用 switchMap
包裹另一个 liveData { }
协程构建器 block 允许我们在调用挂起函数的同时映射数据。协程封装在使用协程构建器创建的新内部 LiveData 中。我相信当他们说“缓存”时,他们指的是这个内部 LiveData 对象,但术语有点模糊。
实现目标 2:
对于数据库植物列表的每次更改,我们已经改用 liveData { }
协程构建器,因此我们可以继续移动此 getOrAwait()
暂停内部 LiveData 内部的函数调用,以便每次都会进行调用。
不过我们仍然遇到了一些问题。每当数据库列表发生变化时,我们都会得到最新的排序顺序,但如果数据库列表没有变化而排序顺序确实发生了变化,那么它就会过时。如果用 LiveData 来解决这个问题会非常复杂。但后来在 CodeLab 中,他们从 LiveData 切换到 Flow,并使用 Flow 的 combine
运算符解决了这个问题。
关于android - 为什么第二个代码可以安全地在 map 中进行网络调用,因为它已被缓存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76688785/