That is a part of the asynchronous circulation collection:
Circulation
is a chilly stream. It emits worth solely when somebody collects or subscribes to it. So it does NOT maintain any information or state.
SharedFlow
is a sizzling stream. It may well emit worth even when nobody collects or subscribes to it. It does NOT maintain any information too.
StateFlow
can also be a sizzling steam. It does NOT emit worth, nevertheless it holds the worth/information.
Circulation Kind | Chilly or Scorching Stream | Information Holder |
Circulation | Chilly | No |
SharedFlow | Scorching (by default) | No |
StateFlow | Scorching (by default) | Sure |
The explanation why
SharedFlow
andStateFlow
are sizzling streams by default, is that they may also be chilly streams relying on the way you create them. See shareIn and stateIn sections under.
What’s information holder?
Information holder (may also be known as as state holder) means it holds information. It retains and shops the final information of the stream, The information can also be observable which permits subscribers to subscribe to it.
There are 3 varieties of information holders in Android improvement
-
LiveData
-
StateFlow
-
State
(Jetpack Compose)
3 of them are fairly related, however they’ve variations. See the desk under.
Information Holder Kind | Android or Kotlin Library? | Lifecycle Conscious? | Required Preliminary Worth? |
LiveData | Android | Sure | No |
StateFlow | Kotlin | No | Sure |
State (Compose) | Android | No | Sure |
StateFlow is Platform Unbiased
LiveData
is Android-specific and ultimately will probably be changed by StateFlow
. Compose state
is just like StateFlow
in my view. Nonetheless, compose State
could be very particular to Android Jetpack Compose. So it’s platform particular, whereas StateFlow
is extra generic and platform impartial.
StateFlow could possibly be life-cycle conscious
LiveData
itself is life-cycle conscious and StateFlow
is NOT. StateFlow
could possibly be life-cycle conscious, relying on the way you acquire it. Compose State
itself is NOT life-cycle conscious. Since it’s utilized by the composable capabilities, when a composable perform leaves composition, it robotically unsubscribes from compose State
.
StateFlow requires Preliminary Worth
Creating LiveData
does NOT require an preliminary worth
val liveData = MutableLiveData<Int>()
however, StateFlow
and compose State
require an preliminary worth.
val stateFlow = MutableStateFlow<Int?>(null)
val composeState: MutableState<Int?> = mutableStateOf(null)
Convert Circulation to SharedFlow
The next instance relies on this circulation in your View Mannequin class
val circulation: Circulation<Int> = circulation {
repeat(10000) { worth ->
delay(1000)
emit(worth)
}
}
and this sharedFlow
variable outlined.
non-public var sharedFlow = MutableSharedFlow<Int>()
1. Circulation.acquire() and SharedFlow.emit()
This converts the Circulation
to SharedFlow
utilizing Circulation<T>.acquire
and manually name the SharedFlow<T>.emit()
.
viewModelScope.launch {
circulation.acquire { worth
-> sharedFlow.emit(worth)
}
}
This can be a sizzling stream. So it emits the worth regardless anybody collects it.
2. Circulation.shareIn()
It’s also possible to use Circulation<T>.shareIn()
to attain the identical consequence.
sharedFlow = circulation.shareIn(
scope = viewModelScope,
began = SharingStarted.Eagerly
)
Nonetheless, when you change SharingStarted.Eagerly
to SharingStarted.WhileSubscribed()
, the SharedFlow
turns into a chilly stream.
Convert Circulation to StateFlow
So we now have this stateFlow
variable outlined within the view mannequin.
non-public val stateFlow = MutableStateFlow<Int?>(null)
1. Circulation.acquire() and StateFlow.worth
This converts the Circulation
to StateFlow
.
viewModelScope.launch {
circulation.acquire { worth ->
stateFlow.worth = worth
}
}
Just like SharedFlow
, this can be a sizzling stream. The distinction is StateFlow
is a knowledge holder and SharedFlow
shouldn’t be.
2. Circulation.stateIn()
It’s also possible to use Circulation<T>.stateIn()
to attain the identical consequence.
stateFlow = circulation.stateIn(
scope = viewModelScope,
began = SharingStarted.Eagerly,
initialValue = null)
Just like Circulation<T>.shareIn()
above, when you change SharingStarted.Eagerly
to SharingStarted.WhileSubscribed()
, the StateFlow
turns into a chilly stream.
Vital be aware: In case you name
Circulation<T>.shareIn()
andCirculation<T>.stateIn()
a number of instances, it creates a number of flows which emit the worth within the background. This ultimately causes pointless useful resource leaks that you just wish to stop.
Accumulate from SharedFlow and StateFlow
Gathering from SharedFlow
and StateFlow
is identical as gathering from the Circulation
. Consult with the next article on other ways of gathering circulation.
1. Accumulate utilizing RepeatOnLifecycle()
The really helpful strategy to acquire Circulation
by Google is utilizing LifeCycle.RepeatOnLifecycle()
. So, we’ll use it for instance.
This instance under converts the stateFlow
to compose state, which is the info holder for composable perform.
@Composable
enjoyable SharedStateFlowScreen() {
val viewModel: StateSharedFlowViewModel = viewModel()
val lifeCycle = LocalLifecycleOwner.present.lifecycle
var composeStateValue by bear in mind { mutableStateOf<Int?>(null) }
LaunchedEffect(true) {
lifeCycle.repeatOnLifecycle(state = Lifecycle.State.STARTED) {
viewModel.stateFlow.acquire { composeStateValue = it }
}
}
}
2. Accumulate utilizing collectAsStateWithLifecycle()
In case you use Android lifecycle Model 2.6.0-alpha01 or later, you’ll be able to scale back the code considerably utilizing Circulation<T>.collectAsStateWithLifecycle()
API
First, it’s essential have this dependency in your construct.gradle
file.
dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha02'
}
Then, you’ll be able to scale back the code to the next. Please be aware that it’s essential specify the @OptIn(ExperimentalLifecycleComposeApi::class)
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
enjoyable SharedStateFlowScreen() {
val viewModel: StateSharedFlowViewModel = viewModel()
val composeStateValue by
viewModel.stateFlow.collectAsStateWithLifecycle()
}
Greatest Practices?
Truthfully, what I discover tough in Android improvement is there are simply means too many choices to perform the identical factor. So which one we should always use? Now, we now have LiveData
, Circulation
, Channel
, SharedFlow
, StateFlow
and compose State
. In what situation, we should always use which one?
So I doc the perfect practices based mostly on varied sources and my interpretations. I have no idea whether or not they make sense. Issues like this are doubtless very subjective too.
-
Do NOT use LiveData particularly when you’re engaged on a brand new venture.
LiveData
is legacy and ultimately will probably be changed byStateFlow
. -
Do NOT expose Circulation straight in View Mannequin, convert it to
StateFlow
as an alternative. This may keep away from pointless workload on the primary UI thread.Circulation
is a chilly stream, it emits information (or restarts the info emission) each time you acquire it. -
Expose StateFlow in your view mannequin as an alternative of compose
State
. SinceStateFlow
is platform impartial, it makes your view mannequin platform impartial which permits you simple migration to KMM (which lets you goal each IOS and Android) for instance.StateFlow
can also be extra highly effective (e.g. it permits you to mix a number of flows into one and many others.) -
Accumulate StateFlow in your UI parts (both in exercise or composable perform) and convert the info to compose
State
. ComposeState
ought to be created and used solely throughout the composable capabilities.
Whether or not ViewModel ought to maintain
StateFlow
or composeState
is questionable. I’ve been utilizing composeState
nevertheless it looks likeStateFlow
is likely to be a greater choice right here based mostly on extra advanced use instances comparable to combining circulation?Then again, if I convert
Circulation
to composeState
in ViewModel straight, I needn’t convert it once more within the composable perform. Why do I would like 2 state/information holders and acquire twice?
What about one-time occasion?
I don’t positive of the use case of SharedFlow
and Channel
. It seems for use as a one-time occasion. When you have one subscriber, you employ Channel
. When you have a number of subscribers, you employ SharedFlow
.
Nonetheless, this text right here by the Google crew form of indicate utilizing SharedFlow
or Channel
as a one-time occasion shouldn’t be really helpful. It’s primarily as a result of they’re sizzling stream, which runs right into a threat of lacking the occasions when the app is within the background or throughout configuration.
It appears to me we should always in all probability simply use StateFlow for every part. Let’s neglect the remainder! Possibly that is simpler this manner…
Supply Code
GitHub Repository: Demo_AsyncFlow (see the SharedStateFlowActivity
)