简体   繁体   English

Kotlin 流与 LiveData

[英]Kotlin Flow vs LiveData

In the last Google I/O, Jose Alcerreca and Yigit Boyar told us that we should no longer use LiveData to fetch data.在上一次 Google I/O 中,Jose Alcerreca 和 Yigit Boyar告诉我们,我们不应该再使用 LiveData 来获取数据。 Now we should use suspend functions for one-shot fetches and use Kotlin's Flow to create a data stream.现在我们应该使用暂停函数进行一次性提取,并使用 Kotlin 的 Flow 创建数据 stream。 I agree that coroutines are great for one-shot fetching or other CRUD operations, such as inserting, etc. But in cases where I need a data stream, I don't understand what advantages Flow gives me.我同意协程非常适合一次性获取或其他 CRUD 操作,例如插入等。但是在我需要数据 stream 的情况下,我不明白 Flow 给了我什么优势。 It seems to me that LiveData is doing the same.在我看来,LiveData 也在做同样的事情。

Example with Flow:流示例:

ViewModel视图模型

val items = repository.fetchItems().asLiveData()

Repository存储库

fun fetchItems() = itemDao.getItems()

Dao

@Query("SELECT * FROM item")
fun getItems(): Flow<List<Item>>

Example with LiveData: LiveData 示例:

ViewModel视图模型

val items = repository.fetchItems()

Repository存储库

fun fetchItems() = itemDao.getItems()

Dao

@Query("SELECT * FROM item")
fun getItems(): LiveData<List<Item>>

I would also like to see some examples of projects using coroutines and Flow to work with the Room or Retrofit.我还想看一些使用协程和 Flow 与 Room 或 Retrofit 一起工作的项目示例。 I found only a Google's ToDo sample where coroutines are used for one-shot fetching and then manually refetch data on changing.我只发现了一个 Google 的ToDo 示例,其中协程用于一次性获取,然后在更改时手动重新获取数据。

Flow is sort of a reactive stream ( like rxjava ). Flow是一种reactive stream (如 rxjava )。 There are a bunch of different operators like .map , buffer() ( anyway less no. Of operator compared to rxJava ).有许多不同的运算符,例如.mapbuffer() (无论如何,与 rxJava 相比,运算符的数量更少)。 So, one of the main difference between LiveData and Flow is that u can subscribe the map computation / transformation in some other thread using因此, LiveDataFlow之间的主要区别之一是您可以使用其他线程订阅 map computation / transformation

 flowOn(Dispatcher....). 

So, for eg:-因此,例如:-

 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 )

With LiveData and map , the above can't be achieved directly !使用LiveDatamap ,以上无法直接实现!

So its recommended to keep flow in the repository level, and make the livedata a bridge between the UI and the repository !因此建议在存储库级别保持流量,并使 livedata 成为 UI 和存储库之间的桥梁!

The main difference is that主要区别在于

  • flow has got a bunch of different operators which livedata doesn't have ! flow有很多不同的操作符,而livedata没有!
  • Generally a regular flow is not lifecycle aware but liveData is lifecyle aware.通常,常规flow不具备生命周期感知能力,但liveData具备生命周期感知能力。 ( we can use stateFlow in conjunction with repeatOnLifecycle to make it lifecycle aware ) (我们可以将 stateFlow 与repeatOnLifecycle结合使用以使其具有生命周期意识)

But again, Its up to u how do u wanna construct your project !但同样,这取决于你如何构建你的项目!

As the name suggests, you can think of Flow like a continuous flow of multiple asynchronously computed values.顾名思义,您可以将 Flow 视为多个异步计算值的连续流。 The main difference between LiveData and Flow, from my point of view, is that a Flow continuously emits results while LiveData will update when all the data is fetched and return all the values at once.从我的角度来看,LiveData 和 Flow 之间的主要区别在于 Flow 不断地发出结果,而 LiveData 将在获取所有数据时更新并立即返回所有值。 In your example you are fetching single values, which is not exactly what Flow was made for in my opinion.在您的示例中,您正在获取单个值,在我看来,这并不是 Flow 的用途。

I don't have a Room example but let's say you are rendering something that takes time, but you wanna display results while rendering and buffering the next results.我没有 Room 示例,但假设您正在渲染一些需要时间的东西,但您想在渲染和缓冲下一个结果时显示结果。

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

Then in your 'Playback' function you can for example display the results where stuffToPlay is a List of objects to render, like:然后在您的“播放”function 中,您可以例如显示其中 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
        }
}

An important characteristic of Flow is that it's builder code (here render function) only gets executed, when it gets collected, hence its a cold stream. Flow 的一个重要特征是它的构建器代码(这里是渲染函数)仅在它被收集时才被执行,因此它是一个的 stream。

You can also refer to the docs at Asynchronous Flow您还可以参考异步流程中的文档

Considering that Flow is part of Kotlin and LiveData is part of the androidx.lifecycle library, I think that Flow is used as part of the uses cases in clean architecture (without dependencies to the framework).考虑到 Flow 是 Kotlin 的一部分,而 LiveData 是 androidx.lifecycle 库的一部分,我认为 Flow 被用作干净架构中用例的一部分(不依赖于框架)。

LiveData, on the other hand, is lifecycle aware, so is a match with ViewModel另一方面,LiveData 具有生命周期意识,因此与 ViewModel 匹配

I have all my architecture using livedata at this moment, but Flow looks like an interesting topic to study and adopt.目前我的所有架构都使用 livedata,但 Flow 看起来是一个值得研究和采用的有趣话题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM