android 在分页库中使用远程中介时,如何创建自己的分页源?

u7up0aaq  于 2023-04-10  发布在  Android
关注(0)|答案(1)|浏览(123)

我正在尝试创建自己的分页源,因为Room库提供的分页源不适合我。我遇到了这样一个问题,分页源加载前1或2页数据(取决于Remote Mediator是否有时间从数据库中删除数据以进行刷新操作),向下滚动这些页面时,不再加载数据。
我认为问题是分页源不理解远程中介从API下载了新数据。如何解决这个问题?
寻呼源:

class TopFilmsLocalPagingSource(
    private val filmLocalStorage: FilmLocalStorage,
    private val type: TopFilmCategories): PagingSource<Int, Film>() {

    override fun getRefreshKey(state: PagingState<Int, Film>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            val anchorPage = state.closestPageToPosition(anchorPosition)
            anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
        }
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Film> {
        val currentPage = params.key ?: 1
        val offset = (currentPage - 1) * params.loadSize
        val count = params.loadSize

        return try {
            val films = filmLocalStorage.getFilmsByType(offset, count, type)
            val prevKey = if (currentPage == 1) null else currentPage - 1
            val nextKey = if (films.count() < count) null else currentPage + 1

            LoadResult.Page(films, prevKey, nextKey)
        } catch (ex: Exception){
            LoadResult.Error(ex)
        }
    }
}

Remote Mediator(伴随对象中的最后一个字段用于测试):

@OptIn(ExperimentalPagingApi::class)
class FilmRemoteMediator(
    private val filmLocalStorage: FilmLocalStorage,
    private val filmRemoteStorage: FilmRemoteStorage,
    private val type: TopFilmCategories): RemoteMediator<Int, Film>() {

    override suspend fun load(loadType: LoadType, state: PagingState<Int, Film>): MediatorResult {
        return try{
            val loadKey = when (loadType) {
                LoadType.REFRESH -> {
                    1
                }
                LoadType.PREPEND -> {
                    return MediatorResult.Success(endOfPaginationReached = true)
                }
                LoadType.APPEND -> {
                    last += 1
                    last
                }
            }
            val films = when(type){
                TopFilmCategories.TOP_100_POPULAR_FILMS -> filmRemoteStorage.getPopularFilms(loadKey)
                TopFilmCategories.TOP_250_BEST_FILMS -> filmRemoteStorage.getBestFilms(loadKey)
                TopFilmCategories.TOP_AWAIT_FILMS -> filmRemoteStorage.getTopAwaitFilms(loadKey)
            }
            if (loadType == LoadType.REFRESH) {
                filmLocalStorage.refreshFilmsByType(films, type)
                MediatorResult.Success(
                    endOfPaginationReached = films.isEmpty()
                )
            }
            else{
                filmLocalStorage.insertAllFilms(films, type)
                MediatorResult.Success(
                    endOfPaginationReached = films.isEmpty()
                )
            }
        } catch (e: IOException) {
            MediatorResult.Error(e)
        } catch (e: HttpException) {
            MediatorResult.Error(e)
        }
    }

    companion object{
        var last = 1
    }

}

存储库:

class FilmRepositoryImpl @Inject constructor(
    private val filmRemoteStorage: FilmRemoteStorage,
    private val filmLocalStorage: FilmLocalStorage): FilmRepository {

    @OptIn(ExperimentalPagingApi::class)
    override fun getBestFilmsPaged(): Flow<PagingData<DomainFilm>> {
        return Pager(PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 20),
        remoteMediator = FilmRemoteMediator(filmLocalStorage,
            filmRemoteStorage, TopFilmCategories.TOP_250_BEST_FILMS)){
            TopFilmsLocalPagingSource(filmLocalStorage, TopFilmCategories.TOP_250_BEST_FILMS)
        }.flow.toDomain()
    }

}

fun Flow<PagingData<com.gramzin.cinescope.data.model.Film>>.toDomain(): Flow<PagingData<DomainFilm>> {
    return transform { value ->
        emit(value.map {
            it.toDomain()
        })
    }
}

我尝试记录发生的操作:
1.寻呼源:加载页面1
1.远程中介:刷新操作(加载第1页)
1.寻呼源:加载第2页
1.远程中介:加载成功
1.远程中介:前置操作
1.远程中介:追加操作(加载第2页)
1.远程中介:加载成功

wkyowqbh

wkyowqbh1#

您需要添加一个ThreadSafeInvalidationObserver来跟踪数据库中的更改。您还需要从load方法中的observer调用registerIfNecessary方法。

class TopFilmsLocalPagingSource(
private val filmLocalStorage: FilmLocalStorage,
private val type: TopFilmCategories): PagingSource<Int, Film>() {

private val observer = ThreadSafeInvalidationObserver(
    tables = arrayOf(DBUtils.filmsTableName),
    onInvalidated = ::invalidate
)

override fun getRefreshKey(state: PagingState<Int, Film>): Int? {
    return state.anchorPosition?.let { anchorPosition ->
        val anchorPage = state.closestPageToPosition(anchorPosition)
        anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
    }
}

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Film> {
    observer.registerIfNecessary(filmLocalStorage.db)
    val currentPage = params.key ?: 1
    val offset = (currentPage - 1) * params.loadSize
    val count = params.loadSize

    return try {
        val films = filmLocalStorage.getFilmsByType(offset, count, type)
        val prevKey = if (currentPage == 1) null else currentPage - 1
        val nextKey = if (films.count() < count) null else currentPage + 1

        LoadResult.Page(films, prevKey, nextKey)
    } catch (ex: Exception){
        LoadResult.Error(ex)
    }
}
}}

相关问题