Flow/LiveData….What Are They? Best Use Case. (Lets Build a Login System)

Inuwa Ibrahim
ProAndroidDev
Published in
5 min readApr 15, 2021

--

As a modern android developer, you must have came across the word “Live Data” or ”Flow” numerous times.
There are so many articles about them, but I feel most of them ignore the “use” case and a simple example to help beginners.

Live Data

Live data is part of Android Architecture Components which are basically a collection of libraries that help you design robust, testable, and maintainable apps.

These set of libraries contains classes which you can use in your app.
One of these classes is LiveData.

It is an Observable data class — Meaning it can be observed by other components — most profoundly UI controllers (Activities/Fragments). So, instead of having a reference of the activity/fragment in your viewModel( which you shouldn’t have due to leaks), you now have a reference to the viewModel in the activity/fragment

It is Lifecycle aware— Meaning it sends updates to our UI (Activities/Fragments) only when our view is in the active state. (No memory leaks)

Using LiveData provides the following advantages:

  • No memory leaks
  • Ensures your UI matches your data state
  • No crashes due to stopped activities
  • Always up to date data
  • Proper configuration changes
  • Sharing resources

Live Data Use Case: Let’s Build a simple login system.

Using MVVM we will build a simple login system that communicates with an API, making a network call. Let’s see how LiveData Works in a real world app.

When a user inputs his email and password.
On Login click we:

  • Communicate with our viewModel
  • Observe the data in our Activity
  • Display appropriate messages depending on the status of response gotten

We are making use of

Steps

Set up Retrofit for network calls

  • Check full code for setting up retrofit which is used for making network calls. OR Check this article I wrote which explains all in details

Set up Repository

  • Create a new class — LoginRepoLiveData

Note — Our Repo implements/extend a BaseDataSource class that helps to get status of our network call and handles it appropriately

Set up our ViewModel

  • Create a new class LoginViewModelLiveData
  • One of the reasons why LiveData sits in a ViewModel is to survive configuration changes, so that when the device is rotated, you don’t lose the state and the UI isn’t recreated.
  • LiveData has no public method to modify its data. so we use MutableLiveData which can be modified/changed outside the ViewModel class
  • Using coroutines, we have a function that obtain values gotten from network request from the repository

Set up View

In your LiveDataActivity, you have this

  • Note, We are not using dependency Injection(Hilt), so check full code for ViewModelFactory
  • The code above is self explanatory
    We setUp ViewModel
    We Observe our ViewModel and listen to changes

If you did everything well, you should have something like this:

  • After inputting the wrong credentials, I got an error displayed in the snackbar saying ”Invalid Email Or Password”
  • Now, Lets try inserting the right details and lets see what happens… 👌

You would certainly get something like this…

  • Notice how the response from previous operation was shown first (SnackBar), before the Toast Message (“Login was successful”).
  • This will surely lead to Bad UX and was a problem at a point till the android team found a “work around” called “SingleLiveEvent” class.
    Its easy to to implement..Read here
  • With that you have a one-shot operation with LiveData displaying current response in a SnackBar or Toast

Drawbacks Of LiveData

  • Lack of control over the execution context
  • Threading issue especially when used in Repositories
  • Not built on top of Coroutines and Kotlin
  • Lack of seamless data integration across between database and UI especially using Room.
  • Lots of Boiler Plate Codes especially while using Transformations

Flows

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.

Flow can handle streams of values, and transform data in complex multi-threaded ways.

State Flow and Shared Flow

StateFlow and SharedFlow are Flow APIs that enable flows to optimally emit state updates and emit values to multiple consumers.

Read More:
https://developer.android.com/kotlin/flow/stateflow-and-sharedflow

StateFlow and LiveData have similar characteristics, so for this article we’ll be using StateFlow. Please check the article to find best use case for you between the two.

State Flow

StateFlow only returns a value that has been updated. State Flow is similar in concept with Observer of RxJava.
When collecting value from StateFlow, we always get the latest value as it always has a value that makes it read-safe because at any point in time the StateFlow will have a value.
Infact, stateFlow requires an initial value

State Flow Use Case: Refactoring our existing login application

  • Create a new Activity, name it FlowActivity
  • Copy the code of activity_live_data.xml to activity_flow.xml
  • Also Create a new class under viewModel package - LoginRepoFlow, this class basically implements flow and emits results from network call using coroutines
  • Create another new class for our viewModel implementation using Flow — LoginViewModelFlow
  • Notice how we are using a MutableStateFlow instead of MutableLiveData and also StateFlow instead of LiveData, the API’s are similar but StateFlow requires an initial value which I declared as Loading State with an empty (null) data
  • Finally, in your FlowActivity, collect the data of the flow and “observe” 😃
  • Now Run the APP and test.. Works Fine….OR NOT. Did you notice any difference? Surely, No. There are a few drawbacks to this approach of using and collecting flow in the UI.

Flow Drawbacks

  • Lack of support for One — Shot operations with SnackBar (If you notice, the same behaviour we had with LiveData not showing the most recent message on snackbar still applies here).
    Fix - Use Channels
  • 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.
    Fix
    (i) Manually stop collecting the flow (adds boilerplate code 🍽)
    (ii) Convert the flow to LiveData using the asLiveData() function (Durrghh, are we still going back to LiveData? 😶)
    (iii) Use the most recent API’s for collecting flow in a much safer way (Better 😀)

Applying the above fixes, our code becomes:

  • LoginViewModelFlow
    Converted to channels
  • FlowActivity
    Now collecting flow from ViewModel in a safer way using lifecycleOwner.addRepeating job() extension function from lifecycle-runtime-ktx library.

If you did everything well you will have something like this:

  • Now you can get “Non Hacky” a One-Shot events with implementation with Snackbars and Toasts
  • Full support with Kotlin Coroutines and much more

To be honest, there isn’t so much distinction between Flows (StateFlow) and Livedata, but the Android Team seems to be pushing the usage of flow (Obviously having Kotlin at the center of their heart).

Time will tell how long LiveData have to survive.

Thanks for reading.

Full link to code:
https://github.com/ibrajix/FlowLiveData

Contact me
https://linktr.ee/Ibrajix

--

--