【问题标题】:Android Room Paging3 correct approach for dynamic filteringAndroid Room Paging3 动态过滤的正确方法
【发布时间】:2020-12-02 10:37:48
【问题描述】:

我正在研究新的 Android Room Paging 库

   implementation "androidx.paging:paging-runtime-ktx:3.0.0-alpha09"

我的源数据库表大约有 10,000 行,我按名称字段的第一个字符过滤如下:-

@Query("SELECT * from citation_style WHERE citation_style_name LIKE :startsWith ORDER BY citation_style_name ASC")
fun fetch(startsWith: String): PagingSource<Int, CitationStyleDO>

存储库

fun fetch(startsWith: String): Flow<PagingData<CitationStyleDO>> {
    return Pager(
        PagingConfig(pageSize = 60, prefetchDistance = 30, enablePlaceholders = false, maxSize = 200)
    ) { database.citationStyleDao().fetch("$startsWith%") }.flow
}

视图模型

fun fetch(startsWith: String): Flow<PagingData<CitationStyleDO>> {
    return repository.fetch(startsWith).cachedIn(viewModelScope)
}

片段

override fun onStartsWithClicked(startsWith: String) {
    lifecycleScope.launch {
        viewModel.fetch(startsWith).collectLatest { adapter.submitData(it) }
    }
}

每次更改开头字符时重复使用lifecycleScope.launch {...} 是否正确?

我应该是 map{} 还是 switchMap{} 由 StartwWith 的 MutabaleLiveData&lt;String&gt; 触发?

【问题讨论】:

    标签: android android-room android-paging-3


    【解决方案1】:

    这不起作用,因为 submitData 直到 PagingData 失效后才会返回。您可能会遇到竞争场景,其中您启动了多个作业,其中PagingDataAdapter 试图从多个PagingData 收集。

    更“流”的方式是将您的 fetch 调用变成一个流,并将其与您的 Flow&lt;PagingData&gt; 结合起来,每次查询更改时都会自动传播取消。

    其他几件事:

    建议让 Paging 为您进行过滤,因为这样您可以避免每次搜索更改时从 DB 重新获取,并且您可以依靠 Paging 来处理配置更改和恢复状态。

    你应该直接使用viewLifecycleOwner而不是lifecycleScope,因为你不想在fragment的视图被销毁后进行分页工作

    例如,

    视图模型

    val queryFlow = MutableStateFlow("init_query")
    val pagingDataFlow = Pager(...) {
            dao.pagingSource()
        }.flow
        // This multicasts, to prevent combine from refetching
        .cachedIn(viewModelScope)
        .combine(queryFlow) { pagingData, query -> 
            pagingData.filter { it.startsWith(query)
        }
        // Optionally call .cachedIn() here a second time to cache the filtered results. 
    

    片段

    override fun onStartsWithClicked(startsWith: String) {
        viewModel.queryFlow = startsWith
    }
    
    override fun onViewCreated(...) {
         viewLifecycleOwner.lifecycleScope.launch {
    viewModel.pagingDataFlow.collectLatest { adapter.submitData(it) }
    }
    

    注意:如果需要,您绝对可以使用 Room 进行过滤,可能正确的方法是在 queryFlow 上 .flatMapLatest 并返回一个新的 Pager 每个尖,并将查询项传递给 dao 函数返回PagingSource

    视图模型

    queryFlow.flatMapLatest { query ->
        Pager(...) { dao.pagingSource(query) }
            .cachedIn(...)
    }
    

    【讨论】:

    • 过滤以及调用 api 怎么样?我猜你可以做一个 api 调用来保存对 DB 的响应,该响应稍后仍会发送到 PagingSource
    • 这里的API调用是什么意思?您的意思是远程获取更多物品吗?这些发生在分页的不同层上,因此它不会改变您在我的答案示例中应用过滤器运算符的方式。
    • 这里combine的问题是已经被丢弃的页面不会被过滤吧?所以过滤一切的唯一方法就是让空间进行过滤
    • @DennisVA 如果页面被丢弃,它将在 UI 中不可见,如果它被重新插入,过滤器转换将再次运行。 PagingData 转换是增量的,因此您不必担心它们“丢失”页面。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 2011-07-07
    • 2012-06-10
    相关资源
    最近更新 更多