Exploring Android LiveData Usages and Behaviors


That is a part of the asynchronous stream 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 lively. Exercise is lively means the UI is seen both in background (began state) or in foreground (resumed state).

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

Fundamental LiveData Usages

Emit LiveData – setValue() / postValue()

First, you have 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 stream as a result of we do not wish to block the UI / foremost thread.
  • setValue() should be run on foremost thread, postValue() might be on both foremost 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 everytime throughout recomposition. I made this error. Thus, it causes the reminiscence leak – observers development.

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 you do not have to explicitly create it.

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

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

}

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

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

    DisposableEffect(this, lifecycleOwner) {
        val observer = Observer<T> { 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 few 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 foremost 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 is paused / loses focus, you can begin one other clear exercise on prime of your present app.

Add Logging in Observer Object

In an effort to show the info is shipped 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 foremost thread? Sure Sure
Can run in background thread? No Sure
Exercise is created/stopped (not seen in background) Knowledge is NOT despatched to UI Identical as setValue()
Exercise is began/paused (seen in background) Knowledge is shipped to UI Identical as setValue()
Exercise is resumed (seen in foreground) Knowledge is shipped to UI Identical as setValue()
When UI is busy Knowledge is queued and is NOT dropped Knowledge is dropped
Run postValue() in foremost thread N/A No distinction, identical as postValue() operating in background thread – information remains to be dropped when UI is busy.

This testing above is completed by calling the observe() (which does not take away the observer automically). This fashion we are able to observe the precise conduct of LiveData respecting the acitivity lifecycle.

Essentially the most significance distinction is when UI is busy, postValue() drops the info and setValue() would not.

Examine observe() vs observeAsState() Behaviors

In case you use observe(), you have to manually name the removeObserver() API. In case you 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 screen rotation)
  • Leaving composition

Simulate Leaving Composition

To simulate leaving composition, you possibly can implement CommonScreen() composable perform 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 perform with logging info to point whether or not the info is shipped 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 (resulting from LiveData is lifecycle-aware part) Identical as observe()
Exercise is began/paused (seen in background) Observer retains – information is shipped to UI Identical as observe()
Exercise is resumed (seen in foreground) Observer retains – information is shipped 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 shipped to UI (resulting from exercise remains to be lively/seen) Observer is eliminated – Knowledge is shipped 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 assets by NOT operating any execution that does not have any affect on UI.

Crucial 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 applying is alive, the setValue() or postValue() retains emitting the info even the exercise is just not lively (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

Is determined by whether or not navigation part 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 benefit of LiveData is exercise lifecycle-aware, it would not waste any assets 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 remains to be lively/seen). To avoid wasting assets, you employ observeAsState() (for Jetpack Compose in fact) which routinely removes the Observer explicitly when it’s not within the composition loop. Thus, no replace is shipped to the UI.

What about utilizing setValue() or postValue()? I personally 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 foremost thread, so no information is dropped and nonetheless stays asynchronous.

Given how briskly Android developlement has evovled, LiveData might be going to be out of date ultimately and changed with StateFlow – shall be lined in subsequent matter.

Supply Code

GitHub Repository: Demo_AsyncFlow (see the LiveDataActivity)

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles