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()
-
Create a
MutableState
information -
Create an
Observer
object -
Observe the
LiveData
(move within theObserver
object) -
Take away the
Observer
object fromLiveData
@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 theObserver
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 ofLiveData
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 theObserver
object, theObserver
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 withStateFlow
– can be lined within the subsequent subject.
Supply Code
GitHub Repository: Demo_AsyncFlow (see the LiveDataActivity
)