Exploring Android LiveData Usages and Behaviors
Tags: android, kotlin, beginners, jetpackcompose
Published: 2022-11-11 23:00:00

**Simple app to demonstrate Android lifecycle-aware LiveData usages and behaviors - setValue(), postValue(), observe(), and observeAsState().**

This is part of the asynchronous flow series:
- **Part 1 - Exploring Android LiveData Usages and Behaviors**
- [Part 2 - Introduction to Kotlin Flows and Channels](https://vtsen.hashnode.dev/introduction-to-kotlin-flows-and-channels)
- [Part 3 - Exploring Different Ways to Collect Kotlin Flow](https://vtsen.hashnode.dev/exploring-different-ways-to-collect-kotlin-flow)
- [Part 4 - Convert Flow to SharedFlow and StateFlow](https://vtsen.hashnode.dev/convert-flow-to-sharedflow-and-stateflow)
- [Part 5 - Flow, SharedFlow, StateFlow Class Diagram](https://vtsen.hashnode.dev/flow-sharedflow-stateflow-class-diagram)

Android `LiveData` is observable and lifecycle-aware data. 

Assuming the lifecycle owner is an activity,  lifecycle-aware means it will only send updates to the UI when the activity is active. Activity is active means the UI is visible either in the background (started state) or in the foreground (resumed state).

`LiveData` also automatically removes all `Observer` objects when the activity is destroyed.

## Basic LiveData Usages

### Emit LiveData - setValue() / postValue()
First, you need to

**Create MutableLiveData**

```kotlin
class LiveDataViewModel: ViewModel() {

    private val _liveData = MutableLiveData<Int>()
    val liveData: LiveData<Int> = _liveData
   /*...*/
}

```
To emit value in `LiveData`, you can either use

**MutableLiveData.setValue() / MutableLiveData.value**
```kotlin
viewModelScope.launch {
    repeat(10000) { value ->
        delay(1000)
        _liveData.value = value
    }
}
```
or

**MutableLiveData.postValue()**
```kotlin
viewModelScope.launch(Dispatchers.IO) {
    repeat(10000) { value ->
        delay(1000)	
        _liveData.postValue(value)
    }
}
```

- Coroutine is used to simulate the asynchronous flow because we don't want to block the UI / main thread.
- `setValue()` must be run on main thread, `postValue()` can be on either main or background thread


### Observe LiveData - observe() / observeAsState()

To observe the `LiveData`, you can either manually observe using`LiveData.observe()` or `LiveData.observeAsState()` API. 

**LiveData.observe()**
1.  Create a `MutableState` data
2. Create an `Observer` object
3. Observe the `LiveData` (pass in the `Observer` object)
4. Remove the `Observer` object from `LiveData`

```kotlin
@Composable
fun LiveDataScreen() {
    val viewModel: LiveDataViewModel = viewModel()
    val lifecycleOwner = LocalLifecycleOwner.current
    
    //(1) Create a MutableState data
    val manualObserveLiveDataState:MutableState<Int?> = 
        remember { mutableStateOf(null) }

    //(2) Create an observer object
    val liveDataObserver = remember {
        Observer<Int> { value ->
            manualObserveLiveDataState.value = value
        }
    }
    
    Column {

        Button(onClick = {
            //(3) Observe the LiveData
            viewModel.liveData.observe(lifecycleOwner, liveDataObserver)
        }) {
            Text(text = "Manually Start Observe LiveData")
        }

        Button(onClick = {
            // (4) Remove the observer from LiveData
            viewModel.liveData.removeObserver(liveDataObserver)
        }) {
            Text(text = "Manually Remove Observer")
        }
    }
}

```
> In step 2 above, `remember {}` is required for creating the`Observer` object so we don't recreate the `Observer` object every time during recomposition. I made this mistake. Thus, it causes the memory leak - observers growth.

> `remember {}` is like caching. If you don't know what it is, the article below gives you some examples.

- [Normal Variable vs remember vs remember mutabableStateOf Comparisons](https://vtsen.hashnode.dev/normal-variable-vs-remember-vs-remember-mutabablestateof-comparisons)

**LiveData.observeAsState()**

`LiveData.observeAsState()` returns `MutableState<Int?>` object, so you don't  need to explicitly create it.

```kotlin
@Composable
fun LiveDataScreen() {
    val viewModel: LiveDataViewModel = viewModel()
    
    // Create MutableState by observing the LiveData
    val observeAsStateLiveData = 
        viewModel.liveData.observeAsState(initial = null)

}
```
Internally, it calls the `DisposableEffect()` side-effect. The most important of this effect is the `onDispose()` function which takes care of removing the `Observer` object for you automatically when the effect leaves the composition.
```kotlin
@Composable
fun <R, T : R> LiveData<T>.observeAsState(initial: R): State<R> {
    val lifecycleOwner = LocalLifecycleOwner.current
    val state = remember { mutableStateOf(initial) }

    DisposableEffect(this, lifecycleOwner) {
        val observer = Observer<T> { state.value = it }
        
        observe(lifecycleOwner, observer)
        onDispose { removeObserver(observer) }
    }
    return state
}
```

## Investigate setValue() vs postValue() Behaviors
To study the behavior `setValue()` vs `postValue()`, these are a few scenarios to try:
- Activity is  created/stopped (not visible in background)
- Activity is started/paused (visible in background)
- Activity is resumed (visible in foreground)
- When UI is busy
- Run `postValue()` in main thread
 
### Simulate UI is Busy

To simulate UI is busy, you can either emit the value fast (e.g. every 1 ms) 

```kotlin
job = viewModelScope.launch {
    repeat(10000) { value ->
         delay(1)
        _liveData.postValue = value
    }
}
```

or simply call `Thread.sleep()` - I prefer this method.

```kotlin
Button(onClick = {
    Thread.sleep(3000)
}) {
    Text(text = "Simulate Busy UI")
}
```
### Simulate Activity is Paused (Visible in Background)

To simulate an activity that is paused/loses focus, you can start another transparent activity on top of your current app. 
- [Simulate Activity Loses Focus](https://vtsen.hashnode.dev/activity-and-view-model-lifecycles-demo-app#heading-simulate-activity-loses-focus)

### Add Logging in Observer Object
In order to prove the data is sent to the UI, you add the following logging to the `Observer` object.

```
val liveDataObserver = remember {
    Observer<Int> { value ->
        Log.d(tag, "[ManualObserver]: Assigning $value to manualObserveLiveDataState.value")
        manualObserveLiveDataState.value = value
    }
}
```

### Summary - setValue() vs postValue() 

After performing various scenarios, these are the differences between `setValue()` and `postValue()`. 


| Scenarios | setValue() | postValue() |
| --- | --- | --- |
| Can run in main thread? | Yes | Yes |
| Can run in background thread? | No | Yes |
| Activity is  created/stopped (not visible in background) | Data is NOT sent to UI | Same as `setValue()` |
| Activity is started/paused (visible in background) | Data is sent to UI | Same as `setValue()` |
| Activity is resumed (visible in foreground) | Data is sent to UI | Same as `setValue()` |
| When UI is busy | Data is queued and is NOT dropped | Data is dropped | 
| Run `postValue()` in main thread | N/A | No difference, same as `postValue()` running in background thread - data is still dropped when UI is busy. |

> This testing above is done by calling the `observe()` (which doesn't remove the observer automatically). This way we can observe the actual behavior of  `LiveData` respecting the activity lifecycle.

The most important difference is when UI is busy, `postValue()` drops the data and `setValue()` doesn't.

## Investigate observe() vs observeAsState() Behaviors
If you use `observe()`, you need to manually call the `removeObserver()` API. If you use `observeAsState()`(which uses `DisposableEffect` internally), it automatically calls the `removeObserver()` API when the `DisposableEffect` leaves the composition. 

These are the scenarios to try:
- Activity is created/stopped (not visible in background)
- Activity is started/paused (visible in background)
- Activity is resumed (visible in foreground)
- Activity is destroyed (screen rotation)
- Leaving composition

### Simulate Leaving Composition
![](https://developer.android.com/static/images/jetpack/compose/lifecycle-composition.png)

To simulate leaving composition, you can implement `CommonScreen()` composable function below, which consists of buttons to hide and show the actual composable `content`.  When the `content` is hidden, it simulates the leaving composition.

```kotlin
@Composable
fun CommonScreen(content: @Composable () -> Unit) {
    var showContent by rememberSaveable { mutableStateOf(true) }
    val context = LocalContext.current

    Column(modifier = Modifier.verticalScroll(rememberScrollState())){
        if (showContent) {
            content()

            Button(onClick = {
                showContent = false
            }) {
                Text("Hide Content (Simulate Leaving Composition)")
            }
        }
        else {
            Button(onClick = {
                showContent = true
            }) {
                Text("Show Content")
            }
        }
    }
}
```


### Add Logging in observeAsState()
Let's duplicate `LiveData<T>.observeAsState` to `LiveData<T>.observeAsStateWithLogging()` extension function with logging information to indicate whether the data is sent to UI.

```kotlin
@Composable
fun <R, T: R> LiveData<T>.observeAsStateWithLogging(initial: R): State<R> {
    /*...*/
    DisposableEffect(this, lifecycleOwner) {
        val observer = Observer<T> {
            Log.d(tag, "[ObserveAsState]: Assigning $it to state.value")
            state.value = it
        }
        /*...*/
    }
    return state
}
```

### Summary - observe() vs observeAsState()
After playing around with different scenarios, here is the summary:

| Scenarios | observe() | observerAsState() |
| --- | --- | --- |
| Activity is created/stopped (not visible in background) | `Observer` retains - data is NOT sent to UI (due to `LiveData` is lifecycle-aware component) | Same as `observe()` |
| Activity is started/paused (visible in background) | `Observer` retains - data is sent to UI | Same as `observe()` |
| Activity is resumed (visible in foreground) | `Observer` retains - data is sent to UI | Same as `observe()` |
| Activity is destroyed | `Observer` is removed - data is NOT sent to UI | Same as `observe()` |
| Leaving composition | `Observer` retains - data is sent to UI (due to activity is still active/visible) | `Observer` is removed - Data is sent to UI |

- For `observe()`, since we haven’t put any special code to remove the `Observer` object, the `Observer` always retains during leaving composition. It only gets removed when the activity is  destroyed. 

- Why we want to remove the  `Observer` when activity is not visible? Because it saves resources by NOT running any execution that doesn't have any impact on UI.

The most important difference of `observeAsState()` is it removes the `Observer` when the effects (where the `observeAsState()` is called) leaves the composition.


## LiveData Lifecycle

The `LiveData` lifecycle is tied to the `ViewModel`. Since `ViewModel`  is tied to the activity lifecycle (in this example), `ViewModel` is destroyed when activity is finished / application is exited.  The `LiveData` is destroyed only when `ViewModel` is destroyed.  So as long as the application is alive, the `setValue()` or `postValue()` keeps emitting the data even the activity is not active (e.g. activity is not visible in background).

This is the logging that keeps printing in background in this app example:
```
[ViewModel]: setValue with 1167
```

Depending on whether navigation component is used, the `ViewModel` lifecycle doesn't always tie to the activity lifecycle. To understand the details of `ViewModel` lifecycle, see the following article:

- [Activity and View Model Lifecycles Demo App](https://vtsen.hashnode.dev/activity-and-view-model-lifecycles-demo-app)

## Conclusion

The good thing about `LiveData` is activity lifecycle-aware, it doesn't waste any resources when the UI is not visible (created/ stopped).   `LiveData` also automatically removes all the observers when activity is destroyed.

However, it doesn't work with leaving composition (because the activity is still active/visible). To save resources, you use  `observeAsState()` (for Jetpack Compose of course) which automatically removes the `Observer` explicitly when it is no longer in the composition loop. Thus, no update is sent to the UI.

What about using `setValue()` or `postValue()`? **I prefer to use `setValue()` because it doesn't drop any data when UI is busy**. I can put the data fetching into the background thread and update the `LiveData` in main thread, so no data is dropped and still remains asynchronous.

> Given how fast Android development has evolved, `LiveData` is probably going to be obsolete eventually and replaced with `StateFlow` - will be covered in the next topic.

## Source Code
GitHub Repository: [Demo_AsyncFlow](https://github.com/vinchamp77/Demo_AsyncFlow) (see the `LiveDataActivity`)

---

*Originally published at [https://vtsen.hashnode.dev](https://vtsen.hashnode.dev/exploring-android-livedata-usages-and-behaviors)*.