You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用PagingAdapter.refresh()导致应用崩溃的问题求助

问题分析与修复方案

1. 修复SwipeRefreshLayout的刷新逻辑

你当前调用feedsAdapter.refresh()后立刻关闭刷新状态,会导致刷新未完成就结束动画,还可能引发状态不一致。正确做法是监听PagingAdapter的加载状态,在刷新完成后再关闭:

binding.swipe.setOnRefreshListener {
    feedsAdapter?.refresh()
}

// 监听Adapter加载状态,刷新中保持动画,结束后关闭
feedsAdapter?.addLoadStateListener { loadState ->
    binding.swipe.isRefreshing = loadState.refresh is LoadState.Loading
}

2. 修正PagingSource的分页Key逻辑(核心崩溃原因)

崩溃根源是分页Key计算混淆了页码和偏移量,导致prevKey重复。假设后端接口参数:pageIndex为页码(从0开始),NETWORK_PAGE_SIZE为每页条数,修改如下:

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, FeedsModel> {
    val pageIndex = params.key ?: STARTING_KEY
    return try {
        val response = restApi.getAllFeeds(pageIndex, NETWORK_PAGE_SIZE)
        val feedsDetails = response.response?.toDomainFeed() ?: emptyList()

        // 下一页页码:当前页+1,无数据则为null
        val nextKey = if (feedsDetails.isEmpty()) {
            null
        } else {
            pageIndex + 1
        }

        LoadResult.Page(
            data = feedsDetails,
            // 上一页页码:当前页为0则无,否则为当前页-1
            prevKey = if (pageIndex == STARTING_KEY) null else pageIndex - 1,
            nextKey = nextKey
        )
    } catch (exception: IOException) {
        LoadResult.Error(exception)
    } catch (exception: HttpException) {
        LoadResult.Error(exception)
    }
}

同时修正getRefreshKey,确保刷新时能正确获取起始Key:

override fun getRefreshKey(state: PagingState<Int, FeedsModel>): Int? {
    // 从锚点位置找到最近的Page,返回上一页+1作为刷新起始Key,找不到则返回STARTING_KEY
    return state.anchorPosition?.let { anchorPosition ->
        state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
    } ?: STARTING_KEY
}

3. 修正RemoteMediator的分页与刷新逻辑

3.1 统一分页Key规则

RemoteMediator的分页Key要和PagingSource保持一致(用页码,从0开始),修改load方法中的Key计算:

val page = when(loadType){
    LoadType.APPEND -> {
        val remoteKeys = getRemoteKeyForLastItem(state)
        val nextKey = remoteKeys?.nextKey
            ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
        nextKey
    }
    LoadType.PREPEND -> {
        val remoteKeys = getRemoteKeyForFirstTime(state)
        val prevKey = remoteKeys?.prevKey
            ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
        prevKey
    }
    LoadType.REFRESH -> {
        // 下拉刷新直接从第一页加载,清空旧数据
        STARTING_KEY
    }
}

3.2 修正RemoteKeys的存储逻辑

确保存入数据库的prevKey和nextKey是正确的页码:

db.withTransaction {
    if(loadType == LoadType.REFRESH){
        db.remoteKeysDao().clearRemoteKeys()
        db.feedsDaoInterface().clearDB()
    }

    // 当前页的上一页页码:当前页为0则无,否则为当前页-1
    val prevKey = if(page == STARTING_KEY) null else page - 1
    // 当前页的下一页页码:无更多数据则为null,否则为当前页+1
    val nextKey = if(paginationEndReached) null else page + 1
    val keys = feeds.map {
        RemoteKeys(feedsId = it.feedId, prevKey = prevKey, nextKey = nextKey)
    }
    db.remoteKeysDao().insertAll(keys)
    db.feedsDaoInterface().insertAllFeeds(*feeds.toDatabasesModel())
}

3.3 实现正确的getRemoteKeyClosestToCurrentPosition

去掉返回null的逻辑,正确实现该方法用于非刷新场景的定位:

private suspend fun getRemoteKeyClosestToCurrentPosition(
    state: PagingState<Int, FeedsModel>
): RemoteKeys? {
    return state.anchorPosition?.let { position ->
        state.closestItemToPosition(position)?.feedId?.let { id ->
            db.remoteKeysDao().remoteKeysRepoId(id)
        }
    }
}

4. 确保ViewModel的Pager配置正确

确认ITEMS_PER_PAGE和NETWORK_PAGE_SIZE保持一致,避免分页大小不匹配:

// 确保两个参数值一致
const val NETWORK_PAGE_SIZE = 10
const val ITEMS_PER_PAGE = 10

@OptIn(ExperimentalPagingApi::class)
fun getFeeds() = Pager(
    config = PagingConfig(pageSize = ITEMS_PER_PAGE, enablePlaceholders = false),
    remoteMediator = FeedsMediator(restApi, myDatabase),
    pagingSourceFactory = { repository.feedsPagingSource() }
)
    .flow
    .cachedIn(viewModelScope)

关键说明

  • 崩溃核心原因是之前将prevKey设置为当前pageIndex,导致连续Page的prevKey重复,Paging3判定为无效分页逻辑抛出异常。
  • 下拉刷新时,RemoteMediator的REFRESH类型会清空数据库并从第一页加载最新数据,确保新Feed出现在列表顶部。
  • 所有分页逻辑必须统一使用页码(而非偏移量),PagingSource和RemoteMediator的Key规则要完全一致。

内容的提问来源于stack exchange,提问作者Vivek Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 10:50:05