Kotlin Flow 与 LiveData

Dmi*_*kov 23 android kotlin android-livedata kotlin-coroutines kotlinx.coroutines.flow

在上一次 Google I/O 大会上,Jose Alcerreca 和 Yigit Boyar告诉我们,我们不应再使用 LiveData 来获取数据。现在我们应该使用挂起函数进行一次性提取并使用 Kotlin 的 Flow 创建数据流。我同意协程非常适合一次性获取或其他 CRUD 操作,例如插入等。但是在我需要数据流的情况下,我不明白 Flow 给我带来了什么优势。在我看来,LiveData 也在做同样的事情。

流程示例:

视图模型

val items = repository.fetchItems().asLiveData()
Run Code Online (Sandbox Code Playgroud)

存储库

fun fetchItems() = itemDao.getItems()
Run Code Online (Sandbox Code Playgroud)

@Query("SELECT * FROM item")
fun getItems(): Flow<List<Item>>
Run Code Online (Sandbox Code Playgroud)

LiveData 示例:

视图模型

val items = repository.fetchItems()
Run Code Online (Sandbox Code Playgroud)

存储库

fun fetchItems() = itemDao.getItems()
Run Code Online (Sandbox Code Playgroud)

@Query("SELECT * FROM item")
fun getItems(): LiveData<List<Item>>
Run Code Online (Sandbox Code Playgroud)

我还想看到一些使用协程和 Flow 来处理 Room 或 Retrofit 的项目示例。我只找到了一个 Google 的ToDo 示例,其中协程用于一次性获取,然后在更改时手动重新获取数据。

San*_*Sur 14

Flow有点reactive stream像(比如 rxjava )。有很多不同的运算符,例如.map, buffer()(与 rxJava 相比,运算符更少)。因此,LiveData和之间的主要区别之一Flow是您可以computation / transformation使用其他线程订阅地图

 flowOn(Dispatcher....). 
Run Code Online (Sandbox Code Playgroud)

所以,例如:-

 flowOf("A","B","C").map { compute(it) }.flowOn(Dispatchers.IO).collect {...} // U can change the execution thread of the computation ( by default its in the same dispatcher as collect )
Run Code Online (Sandbox Code Playgroud)

LiveDatamap,不能直接实现上面的!

因此,建议在存储库级别保持流程,并使实时数据成为 UI 和存储库之间的桥梁!

主要区别在于

  • 一般常规flow没有生命周期的认识,但是liveData 生命周期知道。(我们可以结合使用 stateFlowrepeatOnLifecycle使其具有生命周期意识)
  • flow有一堆不同的运算符,它们livedata没有!

但同样,这取决于你想如何构建你的项目!

  • 值得注意的是,协程现在具有 [StateFlow](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/index.html) 类。该类与 LiveData 类非常相似,因为可以从任何上下文直接访问该值,并且任何更改都会自动发送给任何观察者。但因为它是一个 Flow 对象,这意味着所有好的协程运算符都可用。 (2认同)

小智 7

考虑到 Flow 是 Kotlin 的一部分,LiveData 是 androidx.lifecycle 库的一部分,我认为 Flow 被用作干净架构中用例的一部分(不依赖于框架)。

另一方面,LiveData 具有生命周期感知能力,因此与 ViewModel 相匹配

目前我的所有架构都使用实时数据,但 Flow 看起来是一个值得研究和采用的有趣主题。


svo*_*oid 5

顾名思义,您可以将 Flow 视为多个异步计算值的连续流。在我看来,LiveData 和 Flow 之间的主要区别在于 Flow 不断地发出结果,而 LiveData 将在获取所有数据时更新并立即返回所有值。在您的示例中,您正在获取单个值,在我看来,这并不是 Flow 的真正目的。

我没有 Room 示例,但假设您正在渲染需要时间的内容,但您想在渲染和缓冲下一个结果时显示结果。

private fun render(stuffToPlay: List<Any>): Flow<Sample> = flow {
     val sample = Sample()
     // computationally intensive operation on stuffToPlay
     Thread.sleep(2000)
     emit(sample)
}
Run Code Online (Sandbox Code Playgroud)

然后在您的“播放”功能中,您可以例如显示结果,其中 stuffToPlay 是要呈现的对象列表,例如:

playbackJob = GlobalScope.launch(Dispatchers.Default) {

    render(stuffToPlay)
        .buffer(1000)   // tells the Flow how many values should be calculated in advance

        .onCompletion {
            // gets called when all stuff got played
        }
        .collect{sample ->
           // collect the next value in the buffered queue
           // e.g. display sample
        }
}
Run Code Online (Sandbox Code Playgroud)

Flow 的一个重要特征是它的构建器代码(这里是渲染函数)只有在收集时才会执行,因此它是一个冷流

您还可以参考Asynchronous Flow 中的文档