分页库只在文件室数据库中存储第一次提取的值



我有一个应用程序,我在其中实现了分页库3来从api中提取数据并对其进行分页,它可以很好地提取数据,下一个实现是将提取的数据存储在房间数据库中,我创建了remotemediator类并编写了存储数据的代码,但问题是,它只存储第一页的值(例如,在我的情况下,im使用moviedbapi,提取的每个页面都有20部电影,并且有很多页面(,在我情况下,它只保存前20部电影。即使在我滚动时,它也没有存储更多的数据。我已经实现了完全相同的代码,但似乎是这样,我在一个旧项目中遇到了它,现在是这个项目,我需要一些帮助,提前谢谢你。

  • 电影刀
@Dao
interface MoviesDao {
@Query("SELECT * FROM movieTable ORDER BY id")
fun getMovies() : PagingSource<Int,Result>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertMovies(result: List<Result>)

@Query("DELETE FROM movieTable")
suspend fun clearMovies()

}
  • 遥控键刀
@Dao
interface RemoteKeysDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(remoteKey: List<RemoteKeys>)
@Query("SELECT * FROM remote_keys WHERE movieId = :movieId")
suspend fun remoteKeysRepoId(movieId : Long): RemoteKeys?
@Query("DELETE FROM remote_keys")
suspend fun clearRemoteKeys()

}
  • RemoteMediator类
private  var MOVIES_API_STARTING_PAGE_INDEX = 1
@ExperimentalPagingApi
class MoviesMediator(
private var authResponse: AuthResponse,
private  var movieDatabase: MovieDatabase
) : RemoteMediator<Int,Result>() {
override suspend fun load(loadType: LoadType, state: PagingState<Int, Result>): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: MOVIES_API_STARTING_PAGE_INDEX
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
val prevKey = remoteKeys?.prevKey
if (prevKey == null) {
return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
prevKey
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
val nextKey = remoteKeys?.nextKey
if (nextKey == null) {
return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
nextKey
}
}
try {
val response = authResponse.getMovies(Constants.API_KEY, Constants.LANGUAGE, page).results

val endOfPagination = response.isEmpty()
movieDatabase.withTransaction {
// clear all tables in the database
if (loadType == LoadType.REFRESH) {
movieDatabase.remoteKeysDao().clearRemoteKeys()
movieDatabase.MovieDao().clearMovies()
}
val prevKey = if (page == MOVIES_API_STARTING_PAGE_INDEX) null else page - 1
val nextKey = if (endOfPagination) null else page + 1
val keys = response.map {
RemoteKeys(movieId = it.movieID, prevKey = prevKey, nextKey = nextKey)
}
movieDatabase.remoteKeysDao().insertAll(keys)
movieDatabase.MovieDao().insertMovies(response)
}
return MediatorResult.Success(endOfPaginationReached = endOfPagination)
} catch (ex: Exception) {
return MediatorResult.Error(ex)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Result>): RemoteKeys? {
// Get the last page that was retrieved, that contained items.
// From that last page, get the last item
return state.pages.firstOrNull() { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { movieId ->
// Get the remote keys of the last item retrieved
movieDatabase.remoteKeysDao().remoteKeysRepoId(movieId.movieID)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(state: PagingState<Int, Result>): RemoteKeys? {
// The paging library is trying to load data after the anchor position
// Get the item closest to the anchor position
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.movieID?.let { movieId ->
movieDatabase.remoteKeysDao().remoteKeysRepoId(movieId = movieId)
}
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Result>): RemoteKeys? {
// Get the last page that was retrieved, that contained items.
// From that last page, get the last item
return state.pages.lastOrNull() { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { repo ->
// Get the remote keys of the last item retrieved
movieDatabase.remoteKeysDao().remoteKeysRepoId(movieId = repo.movieID)
}
}
}
  • 将RemoteMediator传递到分页数据
val dataFlow : kotlinx.coroutines.flow.Flow<PagingData<Result>> =
Pager(getPagingConfig(),
remoteMediator = MoviesMediator(authResponse,movieDatabase)){
MoviePagingSource(authResponse)
}.flow
.cachedIn(viewModelScope)
  • 在MainActivity中显示数据

@ExperimentalPagingApi
private fun setUpAdapterOnline(){
moviesAdapter = MoviesAdapter()
lifecycleScope.launchWhenStarted {
moviesModel.dataFlow.collectLatest {
moviesAdapter.submitData(it)
}
}
binding.recycler.adapter = moviesAdapter
binding.recycler.adapter =  moviesAdapter.withLoadStateHeaderAndFooter(
header = LoadingStateAdapter { moviesAdapter.retry() },
footer = LoadingStateAdapter { moviesAdapter.retry() }
)
}

更新

我试着用这个NewsApi而不是这个Unsplash Api。尽管NewsApi提供了per_page作为查询参数,但我仍然面临着类似的问题,即只有first 2页加载,之后在调试时,它表明Load方法中的APPEND块被反复调用,并且分配给page的值总是2。因此,next pages根本不会被提取。

原始答案

@dlam是对的,您需要遵循这里的单一真相来源原则,并且只从MoviesDao内部的Paging Source请求数据。我没有找到你问题的确切解决方案,但我确实有一些发现可以分享。

我想说的是,我在这里的示例代码实验室中调试了Remote Mediator,我发现它实际上在滚动时增加了page。然而,当从您发送给我的github链接调试代码时,page的值永远不会增加,MoviesMediator中的APPEND块会反复执行,即使不滚动。我试着找到原因,唯一能想到的就是你正在使用的Api。我尝试了同样的功能,从network保存到database,但使用Unsplash Api,我也可以在脱机时查看所有页面(而不仅仅是一个页面,与您的情况不同(。

我不确定这是否是codelab在Network/Api Service接口内作为argument传递的per_page参数。在我的例子中,Unsplash Api还允许传递per_page作为参数。

我在这里附上一个链接到我的github存储库。试着比较一下结果,然后再提出任何疑问。

相关内容

最新更新