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()
- 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 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 ofLiveData
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 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 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 withStateFlow
– shall be lined in subsequent matter.
Supply Code
GitHub Repository: Demo_AsyncFlow (see the LiveDataActivity
)