PageKeyedDataSource loadAfter 连续调用
PageKeyedDataSource loadAfter called continuously
我有一个持续调用loadAfter的PageKeyedDataSource,并且所有项目都被多次添加到Recyclerview中。从 API 方面来看,一个 null lastEvaluatedKey
意味着给我第一页,这对于为什么它一直调用以获取第一页但是 A 有点有意义。如果没有更多数据可以停止,它不应该停止吗?得到(又名 params.key == null
?和 B。适配器中的 COMPARATOR
不应该禁止多次添加相同的项目吗?我错过了什么?
PageKeyedDataSource.kt
class ReservationsPageKeyedDataSource(private val retryExecutor: Executor) : PageKeyedDataSource<String, Reservation?>() {
private var retry: (() -> Any)? = null
val initialLoad = MutableLiveData<PagingNetworkState>()
fun retryAllFailed() {
val prevRetry = retry
retry = null
prevRetry?.let {
retryExecutor.execute {
it.invoke()
}
}
}
override fun loadInitial(
params: LoadInitialParams<String>,
callback: LoadInitialCallback<String, Reservation?>
) {
val request = Api.reservationsService.getReservations(dateType = RERVATIONS_DATE_TYPE.future, last = null)
initialLoad.postValue(PagingNetworkState.LOADING)
// triggered by a refresh, execute in sync
try {
val response = request.execute()
val originalData = response.body()?.result?.reservations
val data = mutableListOf<Reservation>()
// some data munipulation
retry = null
initialLoad.postValue(PagingNetworkState.LOADED)
callback.onResult(
data.toList(),
null,
response.body()?.result?.lastEvaluatedKey.toString()
)
} catch (ioException: IOException) {
retry = {
loadInitial(params, callback)
}
val error = PagingNetworkState.error(ioException.message ?: "unknown error")
initialLoad.postValue(error)
}
}
override fun loadBefore(
params: LoadParams<String>,
callback: LoadCallback<String, Reservation?>
) {
// no-op
}
override fun loadAfter(
params: LoadParams<String>,
callback: LoadCallback<String, Reservation?>
) {
// I tried adding an if statement here to check if the params.key is null or not but that didn't help
Api.reservationsService.getReservations(dateType = RERVATIONS_DATE_TYPE.future, last = params.key)
.enqueue(object : Callback<ReservationListResponse> {
override fun onFailure(call: Call<ReservationListResponse>, t: Throwable) {
retry = { loadAfter(params, callback) }
}
override fun onResponse(
call: Call<ReservationListResponse>,
response: Response<ReservationListResponse>
) {
if (response.isSuccessful) {
val data = response.body()?.result?.reservations
retry = null
callback.onResult(
data.orEmpty(),
response.body()?.result?.lastEvaluatedKey.toString()
)
} else {
retry = { loadAfter(params, callback) }
}
}
})
}
}
PagedListAdapter 中的比较器:
companion object {
val COMPARATOR = object : DiffUtil.ItemCallback<Reservation>() {
override fun areContentsTheSame(oldItem: Reservation, newItem: Reservation): Boolean =
oldItem == newItem
override fun areItemsTheSame(oldItem: Reservation, newItem: Reservation): Boolean =
oldItem.id == newItem.id
}
}
大部分代码看起来都不错,但是您通过 toString 将响应的下一个标记部分转换为字符串并将其用作密钥似乎很奇怪。
而不是 PageKeyedDataSource<String, Reservation?>
,尝试 PageKeyedDataSource<KeyType, Reservation>
(不确定上面代码中那个键的类型)。
然后你可以直接从API中取出下一个token,不加修改直接传到你的API的last
参数中。
您还应该使用不可为空的 Reservation
- Paging 库期望加载的项目是非空的,因为保留空值用于表示占位符:https://developer.android.com/reference/androidx/paging/DataSource#implementing-a-datasource
另一个可能的原因是,如果您在 ScrollView 内部使用 Recycleview,loadAfter 将被无限调用,直到数据完成。
Recycleview会自动处理滚动,所以不要在scrollView中使用它。
我有一个持续调用loadAfter的PageKeyedDataSource,并且所有项目都被多次添加到Recyclerview中。从 API 方面来看,一个 null lastEvaluatedKey
意味着给我第一页,这对于为什么它一直调用以获取第一页但是 A 有点有意义。如果没有更多数据可以停止,它不应该停止吗?得到(又名 params.key == null
?和 B。适配器中的 COMPARATOR
不应该禁止多次添加相同的项目吗?我错过了什么?
PageKeyedDataSource.kt
class ReservationsPageKeyedDataSource(private val retryExecutor: Executor) : PageKeyedDataSource<String, Reservation?>() {
private var retry: (() -> Any)? = null
val initialLoad = MutableLiveData<PagingNetworkState>()
fun retryAllFailed() {
val prevRetry = retry
retry = null
prevRetry?.let {
retryExecutor.execute {
it.invoke()
}
}
}
override fun loadInitial(
params: LoadInitialParams<String>,
callback: LoadInitialCallback<String, Reservation?>
) {
val request = Api.reservationsService.getReservations(dateType = RERVATIONS_DATE_TYPE.future, last = null)
initialLoad.postValue(PagingNetworkState.LOADING)
// triggered by a refresh, execute in sync
try {
val response = request.execute()
val originalData = response.body()?.result?.reservations
val data = mutableListOf<Reservation>()
// some data munipulation
retry = null
initialLoad.postValue(PagingNetworkState.LOADED)
callback.onResult(
data.toList(),
null,
response.body()?.result?.lastEvaluatedKey.toString()
)
} catch (ioException: IOException) {
retry = {
loadInitial(params, callback)
}
val error = PagingNetworkState.error(ioException.message ?: "unknown error")
initialLoad.postValue(error)
}
}
override fun loadBefore(
params: LoadParams<String>,
callback: LoadCallback<String, Reservation?>
) {
// no-op
}
override fun loadAfter(
params: LoadParams<String>,
callback: LoadCallback<String, Reservation?>
) {
// I tried adding an if statement here to check if the params.key is null or not but that didn't help
Api.reservationsService.getReservations(dateType = RERVATIONS_DATE_TYPE.future, last = params.key)
.enqueue(object : Callback<ReservationListResponse> {
override fun onFailure(call: Call<ReservationListResponse>, t: Throwable) {
retry = { loadAfter(params, callback) }
}
override fun onResponse(
call: Call<ReservationListResponse>,
response: Response<ReservationListResponse>
) {
if (response.isSuccessful) {
val data = response.body()?.result?.reservations
retry = null
callback.onResult(
data.orEmpty(),
response.body()?.result?.lastEvaluatedKey.toString()
)
} else {
retry = { loadAfter(params, callback) }
}
}
})
}
}
PagedListAdapter 中的比较器:
companion object {
val COMPARATOR = object : DiffUtil.ItemCallback<Reservation>() {
override fun areContentsTheSame(oldItem: Reservation, newItem: Reservation): Boolean =
oldItem == newItem
override fun areItemsTheSame(oldItem: Reservation, newItem: Reservation): Boolean =
oldItem.id == newItem.id
}
}
大部分代码看起来都不错,但是您通过 toString 将响应的下一个标记部分转换为字符串并将其用作密钥似乎很奇怪。
而不是 PageKeyedDataSource<String, Reservation?>
,尝试 PageKeyedDataSource<KeyType, Reservation>
(不确定上面代码中那个键的类型)。
然后你可以直接从API中取出下一个token,不加修改直接传到你的API的last
参数中。
您还应该使用不可为空的 Reservation
- Paging 库期望加载的项目是非空的,因为保留空值用于表示占位符:https://developer.android.com/reference/androidx/paging/DataSource#implementing-a-datasource
另一个可能的原因是,如果您在 ScrollView 内部使用 Recycleview,loadAfter 将被无限调用,直到数据完成。 Recycleview会自动处理滚动,所以不要在scrollView中使用它。