I have a MediatorLiveData that uses three LiveData sources. When any of them emits a new value and I have at least one of each, I use the three values to produce the output for the UI.
Two of the sources are user settings for how to sort and filter a list, and the third is the list data, pulled from a Room database Flow.
It looks something like this:
val thingsLiveData: LiveData<List<Thing>> = object: MediatorLiveData<List<Thing>>() {
var isSettingA: Boolean = true
var settingB: MySortingEnum = MySortingEnum.Alphabetical
var data: List<Thing>? = null
init {
addSource(myRepo.thingsFlow.asLiveData()) {
data = it
dataToValue()
}
addSource(settingALiveData) {
isSettingA= it
dataToValue()
}
addSource(settingBLiveData) {
settingB= it
dataToValue()
}
}
private fun dataToValue() {
data?.let { data ->
viewModelScope.launch {
val uiList = withContext(Dispatchers.Default) {
produceUiList(data, isSettingA, settingB)
}
value = listItems
}
}
}
}
I'm looking for a clean way to convert this to a SharedFlow, preferably without any @ExperimentalCoroutinesApi
. The only SharedFlow builder function I've come across is callbackFlow
, which isn't applicable. Are you intended to use flow { ... }.asSharedFlow(...)
in most cases, and if so, what would that look like here?
The two settings LiveData I also plan to migrate to flows.
The source Flows can be combined using combine()
, which creates a cold Flow that, when collected, will start collecting from its source Flows, which may be hot or cold.
I was originally thinking that I must be missing something and that there should be some way to directly combine hot Flows into a combined hot Flow. But I realized it makes sense that the operators should only return cold Flows and leave it up to you to convert it back to a hot Flow if that's what you need.
In many cases, such as mine, it's perfectly fine to leave it cold. I only collect this Flow from one place in my UI, so it doesn't matter that it only starts combining the sources when it's collected. The source hot Flows don't care whether something is currently collecting them or not...they just keep emitting regardless.
If I collected this Flow from multiple places or multiple times, then it might make sense to use shareIn
on the combined Flow to make it hot, which would avoid redundant work of combining the sources. The potential downside would be that it would combine those sources even when nothing is collecting, which would be wasted work.
val thingsFlow: Flow<List<Thing>> = combine(
myRepo.thingsFlow,
settingALiveData.asFlow(),
settingBLiveData.asFlow()
) { data, isSettingA, settingB -> produceUiList(data, isSettingA, settingB) }
// where produceUiList is now a suspend function that wraps
// blocking code using withContext
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With