android - InvalidObjectException ("Remote key and the prevKey should not be null") 创建 RemoteMediator android

标签 android kotlin pagination android-room android-paging

我正在尝试从 api 响应中缓存列表并将数据与服务器同步,我跟进了 Codelab创建 single source of truth作为安卓documentation使用 paging 3 显示,我按照这些步骤操作,结果令我感到惊讶,但是当我尝试缓存分页数据时,我在第一次运行并继续工作时遇到了这个抛出的错误当应用程序崩溃或第一次运行时没有互联网连接
InvalidObjectException("Remote key and the prevKey should not be null")
它的接缝来自:

private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, MohItem>): RemoteKeys? {
        return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
            ?.let { mohItem ->
                database.remoteKeysDao().remoteKeysId(mohItem.id)
            }
    }


这对我来说很方便,因为它试图插入不是从空列表或空列表中接收到的键,因为出现了截断的代码:

@ExperimentalPagingApi
class MohRemoteMediator(
    private val context: Context,
    private val database: IbnsinaDatabase,
    private val apiService: ApiService,
    private val query: MohSearchQueryRequest

) : RemoteMediator<Int, MohItem>() {
    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, MohItem>
    ): MediatorResult {
        val page = when (loadType) {
            LoadType.REFRESH -> {
                val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
                remoteKeys?.nextKey?.minus(1) ?: Constants.PAGING_STARTING_PAGE_INDEX
            }
            LoadType.PREPEND -> {
                val remoteKeys = getRemoteKeyForFirstItem(state)
                    ?: throw InvalidObjectException("Remote key and the prevKey should not be null")
                remoteKeys.prevKey ?: return MediatorResult.Success(endOfPaginationReached = true)
                remoteKeys.prevKey
            }
            LoadType.APPEND -> {
                val remoteKeys = getRemoteKeyForLastItem(state)
                if (remoteKeys?.nextKey == null) {
                    throw InvalidObjectException("Remote key should not be null for $loadType")
                }
                remoteKeys.nextKey
            }
        }
        try {
            val apiResponse = apiService.getMohList(
                pageIndex = page,
                title = query.title,
                number = query.number,
                month = query.month,
                year = query.year
            )

            val mohs = apiResponse.data ?: emptyList()
            val endOfPaginationReached = mohs.isEmpty()

            database.withTransaction {
                if (loadType == LoadType.REFRESH) {
                    database.remoteKeysDao().clearRemoteKeys()
                    database.mohDao().clearMohs()
                }
                val prevKey = if (page == Constants.PAGING_STARTING_PAGE_INDEX) null else page - 1
                val nextKey = if (endOfPaginationReached) null else page + 1
                val keys = mohs.map {
                    RemoteKeys(mohId = it.id, prevKey = prevKey, nextKey = nextKey)
                }

                database.remoteKeysDao().insertAll(keys)
                database.mohDao().insertAllMohs(mohs)
            }
            return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
        } catch (exception: IOException) {
            return MediatorResult.Error(Throwable(context.getString(R.string.no_internet_connection)))
        } catch (exception: HttpException) {
            return MediatorResult.Error(exception)
        }
    }

    private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, MohItem>): RemoteKeys? {
        return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
            ?.let { repo ->
                // Get the remote keys of the first items retrieved
                database.remoteKeysDao().remoteKeysId(repo.id)
            }
    }

    private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, MohItem>): RemoteKeys? {
        return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
            ?.let { mohItem ->
                database.remoteKeysDao().remoteKeysId(mohItem.id)
            }
    }

    private suspend fun getRemoteKeyClosestToCurrentPosition(
        state: PagingState<Int, MohItem>
    ): RemoteKeys? {
        return state.anchorPosition?.let { position ->
            state.closestItemToPosition(position)?.id?.let { repoId ->
                database.remoteKeysDao().remoteKeysId(repoId)
            }
        }
    }
}

最佳答案

你真的想在这里支持 PREPEND 还是你加载的第一页总是第一页(没有来自网络的前置)?

RemoteMediator 中的这段代码: val prevKey = if (page == Constants.PAGING_STARTING_PAGE_INDEX) null else page - 1

似乎暗示您的 prevKey 在初始 REFRESH 之后将始终为 null,以便在您的加载方法中,

            LoadType.PREPEND -> {
                val remoteKeys = getRemoteKeyForFirstItem(state)
                    ?: throw InvalidObjectException("Remote key and the prevKey should not be null")
                remoteKeys.prevKey ?: return MediatorResult.Success(endOfPaginationReached = true)
                remoteKeys.prevKey
            }

总是会在初始 REFRESH 后抛出?

如果您不支持来自网络的 PREPEND(您仍然可以设置 maxSize 并删除,然后从 PagingSource 重新加载),那么最简单的解决方法是返回 MediatorResult.Success(true ) 立即在 RemoteMediator.load() 中的 PREPEND 上,而不是尝试获取不应该存在的 key 。

关于android - InvalidObjectException ("Remote key and the prevKey should not be null") 创建 RemoteMediator android,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63124535/

相关文章:

mysql - 在不使用 MySQL 中的 LIMIT 和 OFFSET 的情况下在存储过程中进行分页。

Android:如何使其在水平线性布局中的 2 个按钮之间没有空格

Android NDK 示例构建错误 linux

具有数据绑定(bind)的 Android 分页库

java - 如何撤消任何功能

android - 我无法在Android Studio中同步Kotlin Gradle

sql - 如何避免分页计数和数据检索的多次查询?

android - 如何解决此 Implementation() 错误?

Android:动态或以编程方式在一行中添加两个 EditText 并使它们相关

python - 未定义错误: 'url_for_other_page' is undefined