I have a method for fetching Something, let's make it a String for simplicity. The method should return a flow that initially emits the cached string, and then emits the “fresh” value after querying my API.
Thankfully Room emits new data whenever a given table is updated, so that part of the logic works out of the box. I’ve got the refreshing/re-fetching to work as well. But when I try to use .onStart{}
(which IMHO looks a bit cleaner), that’s when both the functionality and my understanding fall apart :/
Here's a proof of concept that should run within IntelliJ or Android Studio without too many unusual dependencies:
// Room automatically emits new values on dbFlow when the relevant table is updated
val dbFlow = MutableStateFlow("cachedValue")
// refresh simulates fetchSomethingFromApi().also { someDao.updateData(it) }
val refresh = suspend {
delay(1000) // simulate API delay
stream.value = "freshValueFromAPI"
}
suspend fun doesNotWork(): Flow<String> = dbFlow
.onStart {
coroutineScope {
launch {
refresh()
}
}
}
suspend fun thisWorks(): Flow<String> = flow {
coroutineScope {
launch {
refresh()
}
dbFlow.collect {
emit(it)
}
}
}
How to test:
runBlocking {
thisWorks().take(2).collect {
println(it)
}
}
or:
runBlocking {
doesNotWork().take(2).collect {
println(it)
}
}
I expect both to produce the same results, however the one with .onStart {}
never emits the cached value, so .take(2)
eventually times out (since it only emits once).
What is going on here?