I'm currently looking for ways to implement sorting on Room
.
Previously, I was using Flow
with "getAll" that contains a MutableList
which becomes a LiveData
on the ViewModel
. Which works perfectly, then I began working on sorting. After researching about "observables", I got an idea that maybe I could use something like StateFlow
so that I just need to emit a new value to the "all" List
when getting all and when sorting. Also, after reading what other things StateFlow
can do, I thought it would be a nice thing to use. But after all the necessary changes I think, it doesn't seem to work.
These are currently my relevant code:
Repo
val allItems: Flow<List<Model>> = flow {
while(true) {
val allItems = dao.getAllSub()
emit(allItems)
}
}
//I don't really use this yet
suspend fun sort(field: String, isAsc: Boolean?): List<Model>{
return dao.sortList(field, isAsc)
}
ViewModel
private val _getAllSub = MutableStateFlow(listOf<Model>())
val getAllSub: StateFlow<List<Model>> = _getAllSub
init {
viewModelScope.launch {
repo.allItems
.catch { exception -> exception.localizedMessage?.let { Log.e("TAG", it) } }
.collect { model ->
_getAllSub.value = model
}
}
}
On my very simple integration test, I'm getting a List is empty
error.
This is my test:
//created a single entity instance
viewModel.insert(model)
val items = viewModel.getAllSub.value
assertThat(items.first()).isEqualTo(model)
Now I'm wondering, did I do something wrong? Did I miss something? Or is my idea even correct or feasible? If someone has a different idea or example of how to properly implement sorting, please let me know. I don't really care about sticking with StateFlow
if there is a better way out there.
@bylazy’s answer shows how to simply return Flow from your DAO. Your current implementation spams the database nonstop (not ok since it monopolizes the disk access) and also blocks a thread nonstop (not ok because it uses a dispatcher thread from the common thread pool constantly, which will be a problem if you have multiple of these going at once).
The second problem could be improved by tacking on .cancellable().flowOn(Dispatchers.IO)
but even then it’s not a good solution to be constantly reading the same thing from the database for no good reason. If you directly return a Flow from your DAO, it only requeries the database when the database is changed, so it is passive and usually in a suspended state.
Your way of turning a Flow into a StateFlow is really convoluted. Just use stateIn
on it. This is equivalent to all of your ViewModel code above:
val getAllSub: StateFlow<List<Model>> =
repo.allItems
.catch { exception -> exception.localizedMessage?.let { Log.e("TAG", it) } }
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())