我在项目中使用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)
}
}
)
}
// ...
}
如果有人帮助解决此问题,那就太好了。
谢谢
答案 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)
}
}
我不知道是否还有其他方法可以做到这一点。如果您知道其他方式,最好将其共享。