HomeAndroidExploring Android LiveData Usages and Behaviors

Exploring Android LiveData Usages and Behaviors


That is a part of the asynchronous circulate sequence:

Android LiveData is observable and lifecycle-aware information.

Assuming the lifecycle proprietor is an exercise, lifecycle-aware means it can solely ship updates to the UI when the exercise is energetic. Exercise is energetic means the UI is seen both within the background (began state) or within the foreground (resumed state).

LiveData additionally routinely removes all Observer objects when the exercise is destroyed.

Primary LiveData Usages

Emit LiveData – setValue() / postValue()

First, it’s worthwhile to

Create MutableLiveData

class LiveDataViewModel: ViewModel() {

    personal val _liveData = MutableLiveData<Int>()
    val liveData: LiveData<Int> = _liveData
   
}

To emit worth in LiveData, you possibly can both use

MutableLiveData.setValue() / MutableLiveData.worth

viewModelScope.launch { 
    repeat(10000) { worth -> 
        delay(1000) _
        liveData.worth = worth 
    } 
}

or

MutableLiveData.postValue()

viewModelScope.launch(Dispatchers.IO) { 
    repeat(10000) { worth -> 
        delay(1000) 
        _liveData.postValue(worth) 
    } 
}
  • Coroutine is used to simulate the asynchronous circulate as a result of we do not wish to block the UI / fundamental thread.

  • setValue() have to be run on fundamental thread, postValue() might be on both fundamental or background thread

Observe LiveData – observe() / observeAsState()

To watch the LiveData, you possibly can both manually observe utilizingLiveData.observe() or LiveData.observeAsState() API.

LiveData.observe()

  1. Create a MutableState information

  2. Create an Observer object

  3. Observe the LiveData (move within the Observer object)

  4. Take away the Observer object from LiveData

@Composable
enjoyable LiveDataScreen() {
    val viewModel: LiveDataViewModel = viewModel()
    val lifecycleOwner = LocalLifecycleOwner.present

    
    val manualObserveLiveDataState:MutableState<Int?> = 
        bear in mind { mutableStateOf(null) }

    
    val liveDataObserver = bear in mind {
        Observer<Int> { worth ->
            manualObserveLiveDataState.worth = worth
        }
    }

    Column {

        Button(onClick = {
            
            viewModel.liveData.observe(lifecycleOwner, liveDataObserver)
        }) {
            Textual content(textual content = "Manually Begin Observe LiveData")
        }

        Button(onClick = {
            
            viewModel.liveData.removeObserver(liveDataObserver)
        }) {
            Textual content(textual content = "Manually Take away Observer")
        }
    }
}

In step 2 above, bear in mind {} is required for creating theObserver object so we do not recreate the Observer object each time throughout recomposition. I made this error. Thus, it causes the reminiscence leak – observers progress.

bear in mind {} is like caching. If you do not know what it’s, the article under provides you some examples.

LiveData.observeAsState()

LiveData.observeAsState() returns MutableState<Int?> object, so that you need not explicitly create it.

@Composable
enjoyable LiveDataScreen() {
    val viewModel: LiveDataViewModel = viewModel()

    
    val observeAsStateLiveData = 
        viewModel.liveData.observeAsState(preliminary = null)

}

Internally, it calls the DisposableEffect() side-effect. A very powerful of this impact is the onDispose() operate which takes care of eradicating the Observer object for you routinely when the impact leaves the composition.

@Composable enjoyable <R, T : R> LiveData.observeAsState(preliminary: R): State { 
    val lifecycleOwner = LocalLifecycleOwner.present 
    val state = bear in mind {   mutableStateOf(preliminary) }

    DisposableEffect(this, lifecycleOwner) { 
        val observer = Observer { state.worth = it }
        observe(lifecycleOwner, observer)

        onDispose {  
            removeObserver(observer) 
        } 
    } 

    return state 
}

Examine setValue() vs postValue() Behaviors

To review the conduct setValue() vs postValue(), these are a number of eventualities to attempt:

  • Exercise is created/stopped (not seen in background)

  • Exercise is began/paused (seen in background)

  • Exercise is resumed (seen in foreground)

  • When UI is busy

  • Run postValue() in fundamental thread

Simulate UI is Busy

To simulate UI is busy, you possibly can both emit the worth quick (e.g. each 1 ms)

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

or just name Thread.sleep() – I choose this technique.

Button(onClick = {
    Thread.sleep(3000)
}) {
    Textual content(textual content = "Simulate Busy UI")
}

Simulate Exercise is Paused (Seen in Background)

To simulate an exercise that’s paused/loses focus, you can begin one other clear exercise on high of your present app.

Add Logging in Observer Object

So as to show the info is distributed to the UI, you add the next logging to the Observer object.

val liveDataObserver = bear in mind {
    Observer<Int> { worth ->
        Log.d(tag, "[ManualObserver]: Assigning $worth to manualObserveLiveDataState.worth")
        manualObserveLiveDataState.worth = worth
    }
}

Abstract – setValue() vs postValue()

After performing numerous eventualities, these are the variations between setValue() and postValue().

Situations setValue() postValue()
Can run in fundamental thread? Sure Sure
Can run in background thread? No Sure
Exercise is created/stopped (not seen in background) Information is NOT despatched to UI Identical as setValue()
Exercise is began/paused (seen in background) Information is distributed to UI Identical as setValue()
Exercise is resumed (seen in foreground) Information is distributed to UI Identical as setValue()
When UI is busy Information is queued and is NOT dropped Information is dropped
Run postValue() in fundamental thread N/A No distinction, identical as postValue() operating in background thread – information continues to be dropped when UI is busy.

This testing above is finished by calling the observe() (which does not take away the observer routinely). This fashion we will observe the precise conduct of LiveData respecting the exercise lifecycle.

A very powerful distinction is when UI is busy, postValue() drops the info and setValue() would not.

Examine observe() vs observeAsState() Behaviors

For those who use observe(), it’s worthwhile to manually name the removeObserver() API. For those who use observeAsState()(which makes use of DisposableEffect internally), it routinely calls the removeObserver() API when the DisposableEffect leaves the composition.

These are the eventualities to attempt:

  • Exercise is created/stopped (not seen in background)

  • Exercise is began/paused (seen in background)

  • Exercise is resumed (seen in foreground)

  • Exercise is destroyed (display rotation)

  • Leaving composition

Simulate Leaving Composition

To simulate leaving composition, you possibly can implement CommonScreen() composable operate under, which consists of buttons to cover and present the precise composable content material. When the content material is hidden, it simulates the leaving composition.

@Composable
enjoyable CommonScreen(content material: @Composable () -> Unit) {
    var showContent by rememberSaveable { mutableStateOf(true) }
    val context = LocalContext.present

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

            Button(onClick = {
                showContent = false
            }) {
                Textual content("Disguise Content material (Simulate Leaving Composition)")
            }
        }
        else {
            Button(onClick = {
                showContent = true
            }) {
                Textual content("Present Content material")
            }
        }
    }
}

Add Logging in observeAsState()

Let’s duplicate LiveData<T>.observeAsState to LiveData<T>.observeAsStateWithLogging() extension operate with logging data to point whether or not the info is distributed to UI.

@Composable
enjoyable <R, T: R> LiveData<T>.observeAsStateWithLogging(preliminary: R): State<R> {
    
    DisposableEffect(this, lifecycleOwner) {
        val observer = Observer<T> {
            Log.d(tag, "[ObserveAsState]: Assigning $it to state.worth")
            state.worth = it
        }
        
    }
    return state
}

Abstract – observe() vs observeAsState()

After enjoying round with completely different eventualities, right here is the abstract:

Situations observe() observerAsState()
Exercise is created/stopped (not seen in background) Observer retains – information is NOT despatched to UI (as a result of LiveData is lifecycle-aware element) Identical as observe()
Exercise is began/paused (seen in background) Observer retains – information is distributed to UI Identical as observe()
Exercise is resumed (seen in foreground) Observer retains – information is distributed to UI Identical as observe()
Exercise is destroyed Observer is eliminated – information is NOT despatched to UI Identical as observe()
Leaving composition Observer retains – information is distributed to UI (as a result of exercise continues to be energetic/seen) Observer is eliminated – Information is distributed to UI
  • For observe(), since we haven’t put any particular code to take away the Observer object, the Observer all the time retains throughout leaving composition. It solely will get eliminated when the exercise is destroyed.

  • Why we wish to take away the Observer when exercise is just not seen? As a result of it saves sources by NOT operating any execution that does not have any impression on UI.

A very powerful distinction of observeAsState() is it removes the Observer when the results (the place the observeAsState() is named) leaves the composition.

LiveData Lifecycle

The LiveData lifecycle is tied to the ViewModel. Since ViewModel is tied to the exercise lifecycle (on this instance), ViewModel is destroyed when exercise is completed / utility is exited. The LiveData is destroyed solely when ViewModel is destroyed. So so long as the appliance is alive, the setValue() or postValue() retains emitting the info even the exercise is just not energetic (e.g. exercise is just not seen in background).

That is the logging that retains printing in background on this app instance: [ViewModel]: setValue with 1167

Relying on whether or not navigation element is used, the ViewModel lifecycle would not all the time tie to the exercise lifecycle. To know the small print of ViewModel lifecycle, see the next article:

Conclusion

The advantage of LiveData is exercise lifecycle-aware, it would not waste any sources when the UI is just not seen (created/ stopped). LiveData additionally routinely removes all of the observers when exercise is destroyed.

Nevertheless, it would not work with leaving composition (as a result of the exercise continues to be energetic/seen). To avoid wasting sources, you utilize observeAsState() (for Jetpack Compose in fact) which routinely removes the Observer explicitly when it’s not within the composition loop. Thus, no replace is distributed to the UI.

What about utilizing setValue() or postValue()? I choose to make use of setValue() as a result of it would not drop any information when UI is busy. I can put the info fetching into the background thread and replace the LiveData in fundamental thread, so no information is dropped and nonetheless stays asynchronous.

Given how briskly Android improvement has advanced, LiveData might be going to be out of date ultimately and changed with StateFlow – can be lined within the subsequent subject.

Supply Code

GitHub Repository: Demo_AsyncFlow (see the LiveDataActivity)

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments