Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. Now we should use suspend functions for one-shot fetches and use Kotlin's Flow to create a data 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. It seems to me that LiveData is doing the same.

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:

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. I found only a Google's ToDo sample where coroutines are used for one-shot fetching and then manually refetch data on changing.

like image 679
Dmitry Simakov Avatar asked Nov 16 '19 11:11

Dmitry Simakov


People also ask

What is difference between LiveData and flow?

StateFlow and LiveData have similarities. Both are observable data holder classes, and both follow a similar pattern when used in your app architecture. The StateFlow and LiveData do behave differently: StateFlow requires an initial state to be passed into the constructor, while LiveData does not.

When should I use SharedFlow?

Use SharedFlow when you need a StateFlow with tweaks in its behavior such as extra buffering, replaying more values, or omitting the initial value.

Why StateFlow is better than LiveData?

StateFlow requires an initial state to be passed in to the constructor, while LiveData does not. LiveData. observe() automatically unregisters the consumer when the view goes to the STOPPED state, whereas collecting from a StateFlow or any other flow does not stop collecting automatically.

What is flow in Kotlin?

In coroutines, a flow is a type that can emit multiple values sequentially, as opposed to suspend functions that return only a single value. For example, you can use a flow to receive live updates from a database. Flows are built on top of coroutines and can provide multiple values.


2 Answers

Flow is sort of a reactive stream ( like rxjava ). There are a bunch of different operators like .map, buffer() ( anyway less no. Of operator compared to 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

 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 !

So its recommended to keep flow in the repository level , and make the livedata a bridge between the UI and the repository !

The main difference is that

  • Generally a regular flow is not lifecycle aware but liveData is lifecyle aware. ( we can use stateFlow in conjunction with repeatOnLifecycle to make it lifecycle aware )
  • flow has got a bunch of different operators which livedata doesn't have !

But again , Its up to u how do u wanna construct your project !

like image 158
Santanu Sur Avatar answered Sep 25 '22 06:09

Santanu Sur


As the name suggests, you can think of Flow like a continuous flow of multiple asynchronously computed values. 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. In your example you are fetching single values, which is not exactly what Flow was dsigned for [update: use StateFlow for that].

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.

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:

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.

You can also refer to the docs at Asynchronous Flow

like image 31
nulldroid Avatar answered Sep 23 '22 06:09

nulldroid