I am new to Android, working on a personal. I am sorting and filtering data fetched from Room. The code has no error but when I run it crashes with the error, “cannot access database on the main thread”.
What I am trying to do is “I am displaying all trains as soon as a user opens the app for that I am using pagination, once data is displayed, the user has a sort row which sorts list by distance, duration, and a filter row which filters list based on ‘from’, ‘to’ stations and train type(“superfast, mail, etc”).
Earlier I was fetching the whole list of trains and then filtering and sorting the entire list, it was taking too much time, to first fetch trains, then filter and sort.
Dao
@Query(
"""
SELECT *
FROM RoomTrains
WHERE (:selectedType IS NULL OR :selectedType = 'All' OR type = :selectedType) AND
(:trainNumberFilter IS NULL OR num = :trainNumberFilter) AND
(:sourceStationFilter IS NULL OR
LOWER(srcName) LIKE '%' || LOWER(:sourceStationFilter) || '%' OR
LOWER(srCode) LIKE '%' || LOWER(:sourceStationFilter) || '%') AND
(:destStationFilter IS NULL OR
LOWER(destName) LIKE '%' || LOWER(:destStationFilter) || '%' OR
LOWER(destCode) LIKE '%' || LOWER(:destStationFilter) || '%')
ORDER BY
CASE WHEN :currentSortColumn = 'dur' AND :currentSortOrder = 1 THEN jourTime END ASC,
CASE WHEN :currentSortColumn = 'dur' AND :currentSortOrder = 0 THEN jourTime END DESC,
CASE WHEN :currentSortColumn = 'dis' AND :currentSortOrder = 1 THEN totalDist END ASC,
CASE WHEN :currentSortColumn = 'dis' AND :currentSortOrder = 0 THEN totalDist END DESC,
CASE WHEN :currentSortColumn = 'num' THEN num END ASC
"""
)
abstract fun getFilteredAndSortedTrains(
selectedType: String?,
trainNumberFilter: String?,
sourceStationFilter: String?,
destStationFilter: String?,
currentSortColumn: String?,
currentSortOrder: Int
): PagingSource<Int, TrnTable>
Repository
fun getFilteredAndSortedTrains(
selectedType: String?,
trainNumberFilter: String?,
sourceStationFilter: String?,
destStationFilter: String?,
currentSortColumn: String?,
currentSortOrder: Boolean
): PagingSource<Int, TrnTable> {
val sortOrder = if (currentSortOrder) 1 else 0
return trainDao.getFilteredAndSortedTrains(
selectedType = if (selectedType == "All") null else selectedType,
trainNumberFilter = if (trainNumberFilter?.trim()
.isNullOrBlank()
) null else trainNumberFilter?.trim(),
sourceStationFilter = if (sourceStationFilter?.trim()
.isNullOrBlank()
) null else sourceStationFilter?.trim(),
destStationFilter = if (destStationFilter?.trim()
.isNullOrBlank()
) null else destStationFilter?.trim(),
currentSortColumn = currentSortColumn,
currentSortOrder = sortOrder
)
}
ViewModel
private val _trains = MutableStateFlow<PagingData<TrnTable>>(PagingData.empty())
val trains: StateFlow<PagingData<TrnTable>> = _trains.asStateFlow()
suspend fun getFilteredAndSortedTrains(
selectedType: String?,
trainNumberFilter: String?,
sourceStationFilter: String?,
destStationFilter: String?,
currentSortColumn: String?,
currentSortOrder: Boolean
) {
viewModelScope.launch(Dispatchers.IO) {
val pager = Pager(
config = PagingConfig(pageSize = 20, enablePlaceholders = false),
pagingSourceFactory = {
chukRepository.getFilteredAndSortedTrains(
selectedType,
trainNumberFilter,
sourceStationFilter,
destStationFilter,
currentSortColumn,
currentSortOrder
)
}
).flow.cachedIn(viewModelScope)
withContext(Dispatchers.IO) {
pager.collect {
_trains.value = it
}
}
}
}
UI
val lazyTrainItems = viewModel.trains.collectAsLazyPagingItems()
TrainListContent(trainList = lazyTrainItems)
fun TrainListContent(trainList: LazyPagingItems<TrnTable>) {
val viewModel = CCViewModel()
var selectedType by remember { mutableStateOf("Rajdhani") }
var trainNumberFilter by remember { mutableStateOf("") }
var sourceStationFilter by remember { mutableStateOf("") }
var destStationFilter by remember { mutableStateOf("") }
var currentSortColumn by remember { mutableStateOf("") }
var currentSortOrder by remember { mutableStateOf(true) }
var clickedTrainNumber by remember { mutableStateOf("") }
val parameters by remember {
derivedStateOf {
selectedType to trainNumberFilter to sourceStationFilter to destStationFilter to currentSortColumn to currentSortOrder
}
}
TrainSearchRow(
selectedType = selectedType,
onSelectedTypeChange = { selectedType = it },
onTrainNumberChange = { trainNumberFilter = it },
onSourceStationChange = { sourceStationFilter = it },
onDestStationChange = { destStationFilter = it }
)
SortRow(
currentSortColumn = currentSortColumn,
currentSortOrder = currentSortOrder,
onSortChange = { column, order ->
currentSortColumn = column
currentSortOrder = order
}
)
LaunchedEffect(parameters) {
withContext(Dispatchers.IO){
viewModel.getFilteredAndSortedTrains(
selectedType,
trainNumberFilter,
sourceStationFilter,
destStationFilter,
currentSortColumn,
currentSortOrder
)
}
}
Is this a good approach to fetching sorted and filtered data from Room?
Again I am new to code and learning how to do it.
Thank you