首页 > 解决方案 > 后台线程上的 Android 实时数据转换

问题描述

我看到了这个,但我不确定如何实现它,或者如果这是同一个问题,我有一个调解器实时数据,当它的 2 个源实时数据更新或基础数据(房间数据库)更新时,它会更新似乎工作正常,但如果数据更新很多,它会快速连续刷新很多,我得到一个错误

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

这不会每次都发生,只有当数据库快速连续更新大量更新时,才会出现视图模型的问题部分,

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)
}

该错误实际上是从函数 searchPokemonForPaging 抛出的

例如,当应用程序启动时会发生大约 300 次写入,但是如果我通过使所有内容暂停并使用 runBlocking 返回 Pager 来强制调用关闭主线程,它确实可以工作并且我不再收到错误但它显然会阻塞ui,那么有没有办法使 switchmap 异步或使 searchAndFilterPokemonPager 方法异步返回寻呼机?我知道第二个在技术上是可行的(从异步返回),但也许协程有办法解决这个问题,
感谢您的帮助

标签: androidandroid-roomandroid-livedatakotlin-coroutinesandroid-paging

解决方案


根据@EpicPandaForce 的建议,我有这个解决方案

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

    searchPokemon = combinedValues.switchMap {
        liveData {
            val search = it?.first ?: return@liveData
            val filters = it.second ?: return@liveData
            withContext(Dispatchers.IO){
                emitSource(searchAndFilterPokemonPager(search, filters.toList()))
            }
        }.distinctUntilChanged()
    }

但我仍然收到此错误

E/ROOM: Cannot run invalidation tracker. Is the db closed?
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

在块中的行 searchPokemonForPaging(search) 上抛出

    @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 searchPokemonForPaging(search: String): PagingSource<Int, PokemonWithTypesAndSpeciesForList> {
    return repository.searchPokemonWithTypesAndSpeciesForPaging(search)
}

回购方法

fun searchPokemonWithTypesAndSpeciesForPaging(search: String): PagingSource<Int, PokemonWithTypesAndSpeciesForList> {
    return pokemonDao.searchPokemonWithTypesAndSpeciesForPaging(search)
}

道法

@Transaction
@Query("SELECT pokemon_id, pokemon_name, pokemon_image_url, pokemon_sprite FROM Pokemon WHERE pokemon_name LIKE :search ORDER BY pokemon_id ASC")
fun searchPokemonWithTypesAndSpeciesForPaging(search: String): PagingSource<Int, PokemonWithTypesAndSpeciesForList>

推荐阅读