Android 后台线程上的实时数据转换

标签 android android-room android-livedata kotlin-coroutines android-paging

我看到了this但我不确定如何实现它,或者如果这是同一个问题,我有一个调解器实时数据,当其 2 个源实时数据更新或底层数据(Room db)更新时,它会更新,它似乎可以工作很好,但是如果数据更新很多,它会快速连续刷新很多,并且我会收到错误

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

这种情况不会每次都会发生,只有当数据库连续快速更新时才会发生,这就是 View 模型的问题部分,

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 返回寻呼机来强制调用关闭主线程,它确实可以工作,并且我不再收到错误,但是它显然会阻塞用户界面,那么有没有办法使 switchmap 异步或使 searchAndFilterPokemonPager 方法异步返回寻呼机?我知道第二个在技术上是可能的(从异步返回),但也许协程有办法解决这个问题
感谢您的帮助

最佳答案

您可以使用 combineTuple (which is available as a library that I wrote for this specific purpose) 简化组合(可选)

之后,您可以使用 liveData { 协程构建器将执行移至后台线程

现在您的代码将如下所示

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

val isFiltersLayoutExpanded: MutableLiveData<Boolean> = getFiltersLayoutExpanded()

init {
    searchPokemon = combineTuple(search, filters).switchMap { (search, filters) -> 
        liveData {
            val search = search ?: return@liveData
            val filters = filters ?: return@liveData

            withContext(Dispatchers.IO) {
                emit(searchAndFilterPokemonPager(search, filters.toList()))
            }
        }
    }.distinctUntilChanged()
}

关于Android 后台线程上的实时数据转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65539628/

相关文章:

java - 在 Android ListView 中添加商品价格

android - 在android中的视频中添加文本水印

java - 设置 TextView 颜色的最有效方法

android - 为什么在多次调用 setValue() 时 MutableLiveData 观察器只被调用一次?

android - 如何创建发出单个事件并仅通知最后订阅的观察者的 LiveData?

android - 无法生成签名的 apk? [发生重复平台类错误]

android - 有没有办法正确模拟房间数据库?

Android - Room Persistence Library - 根据需要同步和通过观察者访问数据

Android Paging 3 与房间

android - 使用实时数据时如何在 Android 中链接转换?