使用Paging3库时更改请求并获取新的数据流

时间:2020-06-24 11:43:37

标签: android android-paging android-paging-library

我在项目中使用Jetpack的Paging 3库来处理数据分页。我有一个用例,当用户更改搜索请求中的某些内容(例如添加/删除某些过滤器)时,我必须调用API并根据新的搜索请求用新数据重新填充列表。但是,当我创建新的Pager实例并将其传递给我的PagingDataAdapter适配器时,它会抛出:

java.lang.IllegalStateException: Collecting from multiple PagingData concurrently is an illegal operation.

我的实现是这样的:


存储库

class Repository {
    fun getDataStream(request: Request): Flow<PagingData<Response>> {
        return Pager(
            config = PagingConfig(
                pageSize = 10,
                initialLoadSize = 10,
                prefetchDistance = 3
            ),
            initialKey = 1,
            pagingSourceFactory = {
                DataPagingSource(
                    request = request,
                    repository = this
                )
            }
        ).flow
    }

    fun getData(page: Int, request: Request): Result<Response> {
        return remoteDataSource.getData(page, request)
    }
}

DataPagingSource

class DataPagingSource(
    private val request: Request,
    private val repository: Repository
) : PagingSource<Int, Response>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Response> {
        val page = params.key ?: 1
        // Result is a sealed class which has two derived classes: Success and Error
        return when (val result = repository.getData(page, request)) {
            is Result.Success -> {
                LoadResult.Page(
                    data = result.data,
                    nextKey = page.inc(),
                    prevKey = null
                )
            }
            is Result.Error -> LoadResult.Error(
                result.error
            )
        }
    }
}

ViewModel

class SomeViewModel(
    private val repository: Repository
): ViewModel() {
    private val _currentRequest = MutableLiveData<Request>()
   
    val data = _currentRequest
        .switchMap {
            repository
                .getDataStream(it)
                .cachedIn(viewModelScope)
                .asLiveData()
        }

    fun updateRequest(request: Request) {
        _currentRequest.postValue(request)
    }
}

片段

class SomeFragment: Fragment() {
    private lateinit var viewModel: SomeViewModel

    // ...
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // ...
        viewModel.data.observe(
            viewLifecycleOwner,
            Observer {
                lifecycleScope.launch {
                    adapter.submitData(it)
                }
            }
        )
    }
    // ...
}

如果有人帮助解决此问题,那就太好了。
谢谢

2 个答案:

答案 0 :(得分:2)

我相信,如果您将Pager视为LiveData,则需要在片段中使用adapter.submitData(lifecycle, data)方法而不是adapter.submitData(data),尽管我也建议您尝试在distinctUntilChanged()上进行转换您的_currentRequest LiveData可以限制从重复的请求中创建多个PagingData对象。

submitData(生命周期:生命周期,pagingData:PagingData)文档

当观察Pager产生的RxJava或LiveData流时,通常使用此方法。对于Flow支持,请使用悬浮的SubmitData重载,它可以通过CoroutineScope自动取消操作,而不是依赖生命周期

https://developer.android.com/reference/kotlin/androidx/paging/PagingDataAdapter#submitdata_1

片段

    viewModel.data.observe(
        viewLifecycleOwner,
        Observer {
            adapter.submitData(lifecycle, it)
        }
    )

ViewModel

    val data = _currentRequest
        // Limit duplicate Requests (Request class should implement equals())
        .distinctUntilChanged()  
        .switchMap {
        // ...
    }

答案 1 :(得分:0)

我找到了解决我问题的方法。为了使Paging库能够使用新的请求模型获取数据,您必须更改请求模型,然后在invalidate上调用PagingDataSource。这是一个示例:
ViewModel 中,代码更改如下:

class SomeViewModel: ViewModel() {    
    private var _dataPagingSource: DataPagingSource? = null
    private val _requestChannel = ConflatedBroadcastChannel<Request>()
    
    val data = Pager(
        config = PagingConfig(
            pageSize = 10,
            initialLoadSize = 10,
            prefetchDistance = 3
        ),
        initialKey = 1,
        pagingSourceFactory = {
            DataPagingSource(
                request = _requestChannel.value,
                repository = repository
            ).also {
                dataSource = it
            }
        }
    ).flow.cachedIn(viewModelScope).asLiveData()

    // ...
    
    // subscribe on requestChannel and invalidate dataSource each time
    // it emits new value
    init {
        _requestChannel
            .asFlow()
            .onEach { _dataPagingSource?.invalidate() }
            .launchIn(viewModelScope)
    }

    // call this method with the new request 
    fun updateRequest(newRequest: Request) {
        _requestChannel.send(newRequest)
    }
}

存储库如下所示:

class Repository {
    // we do not need getDataStream method here anymore

    fun getData(page: Int, request: Request): Result<Response> {
        return remoteDataSource.getData(page, request)
    }
}

我不知道是否还有其他方法可以做到这一点。如果您知道其他方式,最好将其共享。