When I download info from a DataBase the collect function doesn’t stop emission (Kotlin's collect function doesn't terminate itself.)
I have this entity class:
@Entity
data class ShareEntity(
@PrimaryKey(autoGenerate = false)
val secId: String,
val shortName: String,
val fullName: String = "",
val isFavorite: Boolean
)
The function to get all the shares for specific by “isFavorite” in the Dao looks like this:
@Dao
interface DataBaseDao {
………..
@Query("SELECT * FROM ShareEntity WHERE isFavorite =:isFavorite")
fun getAllSharesByIsFavorite(isFavorite: Boolean): Flow<List<ShareEntity>>
}
In the DataBaseDao there are many other functions that insert some data into ShareEntity entity.
For working with the Dao interface the DataBaseRepository and its implementation DataBaseRepositoryIm were made:
interface DataBaseRepository {
………..
fun getAllSharesByIsFavorite(isFavorite: Boolean): Flow<List<ShareEntity>>
}
class DataBaseRepositoryIm(
private val dataBaseDao: DataBaseDao
) : DataBaseRepository{
………..
override fun getAllSharesByIsFavorite(isFavorite: Boolean): Flow<List<ShareEntity>> {
return dataBaseDao.getAllSharesByIsFavorite(isFavorite)
}
}
In my NewsScreenViewModel1 I have a function that loads all the Shares by “isFavorite”
@HiltViewModel
class NewsScreenViewModel1 @Inject constructor(
private val dataBaseRepository: DataBaseRepository,
private val repository: ApiRepository,
) : ViewModel(){
suspend fun downloadListOfSharesByIsFavorite(isFavorite: Boolean = true){
val shares = dataBaseRepository.getAllSharesByIsFavorite(isFavorite).collect{ it:List<ShareEntity> ->
println("NewsScreenVM1: allShares is with isFavorite = $isFavorite are $it")
println("NewsScreenVM1: the size of shares is ${it.size}")
}
println("NewsScreenVM1: Now we are outside of the collect function")
}
}
In the composable NewsScreen I start the function with LauncheEffect:
@Composable
fun NewsScreen(
NewsScreenViewModel1: NewsScreenViewModel1 = hiltViewModel()
) {
LaunchedEffect(key1 = Unit) {
NewsScreenViewModel1.downloadListOfSharesByIsFavorite()
}
}
The problem is that collect function doesn’t terminate itself or in other words it doesn’t stop. In the LogCat I see:
“NewsScreenVM1: allShares is with isFavorite = true are [ShareEntity(secId=ALRS, shortName=АЛРОСА ао, fullName=, isFavorite=true), ShareEntity(secId=AGRO, shortName=AGRO-гдр, fullName=, isFavorite=true), ShareEntity(secId=AFLT, shortName=Аэрофлот, fullName=, isFavorite=true), ShareEntity(secId=FEES, shortName=Россети, fullName=, isFavorite=true)]”
“NewsScreenVM1: the size of shares is 4”
And there is no: "NewsScreenVM1: Now we are outside of the collect function” which means the collect function hasn’t stopped its emission and is still running.
What did I do wrong? Why I can’t execute lines of the code after collecting some data from the database?
I tried to run the flow on a different coroutine scope like this:
fun downloadListOfSharesByIsFavorite(isFavorite: Boolean = true){
viewModelScope.launch {
val shares = dataBaseRepository.getAllSharesByIsFavorite(isFavorite).collect{ it:List<ShareEntity> ->
println("NewsScreenVM1: allShares is with isFavorite = $isFavorite are $it")
println("NewsScreenVM1: the size of shares is ${it.size}")
}
println("NewsScreenVM1: Now we are outside of the collect function")
}
}
But the message, 'NewsScreenVM1: Now we are outside of the collect function,' appears right before all the calculations, which is not what I want. I want it to be the last massage.
When I use async function couple with .await() I don’t get the last message either as the flow never stops. Here is the code:
suspend fun downloadListOfSharesByIsFavorite(isFavorite: Boolean = true) {
val asyncValue = viewModelScope.async {
var size = 0
val shares = dataBaseRepository.getAllSharesByIsFavorite(isFavorite)
.collect { it: List<ShareEntity> ->
println("NewsScreenVM1: allShares is with isFavorite = $isFavorite are $it")
println("NewsScreenVM1: the size of shares is ${it.size}")
size = it.size
}
size
}
println("NewsScreenVM1: Now we are outside of the collect function. The list size is ${asyncValue.await()}")
}
That's the point of using a Flow
. It doesn't end because it's waiting for DB updates. Every time the list changes in DB, you will get the new updated list in collect
. By design it is not meant to end:
https://developer.android.com/codelabs/basic-android-kotlin-training-intro-room-flow
If what you want is to just query the current list once, no need for a Flow
nor a collect
call. Just use a suspend
function that returns a List
.