Monday 4 January 2021

Android live data transformations on a background thread

I saw this but I'm not sure how to implement it or if this is the same issue, I have a mediator live data that updates when either of its 2 source live datas update or when the underlying data (Room db) updates, it seems to work fine but if the data updates a lot it refreshes a lot in quick succession and I get an error

Cannot run invalidation tracker. Is the db closed?
Cannot access database on the main thread since it may potentially lock the UI for a long period of time

this doesn't happen everytime, only when there are a lot of updates to the database in very quick succession heres the problem part of the view model,

var search: MutableLiveData<String> = getSearchState()
val filters: MutableLiveData<MutableSet<String>> = getCurrentFiltersState()
val searchPokemon: LiveData<PagingData<PokemonWithTypesAndSpeciesForList>>

val isFiltersLayoutExpanded: MutableLiveData<Boolean> = getFiltersLayoutExpanded()

init {

    val combinedValues =
        MediatorLiveData<Pair<String?, MutableSet<String>?>?>().apply {
            addSource(search) {
                value = Pair(it, filters.value)
            }
            addSource(filters) {
                value = Pair(search.value, it)
            }
        }

    searchPokemon = Transformations.switchMap(combinedValues) { pair ->
        val search = pair?.first
        val filters = pair?.second
        if (search != null && filters != null) {
            searchAndFilterPokemonPager(search, filters.toList())
        } else null
    }.distinctUntilChanged()
}

@SuppressLint("DefaultLocale")
private fun searchAndFilterPokemonPager(search: String, filters: List<String>): LiveData<PagingData<PokemonWithTypesAndSpeciesForList>> {
    return Pager(
        config = PagingConfig(
            pageSize = 20,
            enablePlaceholders = false,
            maxSize = 60
        )
    ) {
        if (filters.isEmpty()){
            searchPokemonForPaging(search)
        } else {
            searchAndFilterPokemonForPaging(search, filters)
        }
    }.liveData.cachedIn(viewModelScope)
}

@SuppressLint("DefaultLocale")
private fun getAllPokemonForPaging(): PagingSource<Int, PokemonWithTypesAndSpecies> {
    return repository.getAllPokemonWithTypesAndSpeciesForPaging()
}

@SuppressLint("DefaultLocale")
private fun searchPokemonForPaging(search: String): PagingSource<Int, PokemonWithTypesAndSpeciesForList> {
    return repository.searchPokemonWithTypesAndSpeciesForPaging(search)
}

@SuppressLint("DefaultLocale")
private fun searchAndFilterPokemonForPaging(search: String, filters: List<String>): PagingSource<Int, PokemonWithTypesAndSpeciesForList> {
    return repository.searchAndFilterPokemonWithTypesAndSpeciesForPaging(search, filters)
}

the error is actually thrown from the function searchPokemonForPaging

for instance it happens when the app starts which does about 300 writes but if I force the calls off the main thread by making everything suspend and use runBlocking to return the Pager it does work and I don't get the error anymore but it obviously blocks the ui, so is there a way to maybe make the switchmap asynchronous or make the searchAndFilterPokemonPager method return a pager asynchronously? i know the second is technically possible (return from async) but maybe there is a way for coroutines to solve this
thanks for any help



from Android live data transformations on a background thread

No comments:

Post a Comment