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

Jetpack Compose中Paging3 Remote Mediator无滚动无限调用API的修复方法

问题分析与修复方案

你的代码出现无限调用API的问题,核心原因有两个:分页粒度错误和RemoteMediator分页逻辑漏洞,以下是具体修复步骤:


1. 修正分页粒度(最关键问题)

你当前把整个API响应(TopAnimeResponse,包含整页数据和分页信息)作为Paging的条目存在数据库中,但Pager的pageSize=5,这会让Paging误认为只加载了1条数据(因为每个TopAnimeResponse是1个条目),远小于pageSize,因此会持续触发APPEND加载,导致无限循环。

修复方案:

  • 新建单个动漫实体类(比如Anime),对应API响应中data数组的每个元素。
  • 修改数据库Dao,改为存储和查询Anime实体,而非TopAnimeResponse。
  • 调整RemoteMediator的泛型为RemoteMediator<Int, Anime>,加载API后将response.data列表插入数据库。

2. 修复RemoteMediator的分页逻辑漏洞

当前APPEND分支中,lastItem?.pagination?.currentPage?.plus(1)可能返回null,如果API调用时page参数默认被设为1,会导致每次都重复请求第一页,进而触发无限加载。同时,分页状态的跟踪需要更可靠:

修正后的RemoteMediator示例:

@OptIn(ExperimentalPagingApi::class)
class TopAnimeRemoteMediator(
    private val apiService: ApiService,
    private val database: TopAnimeDatabase,
) : RemoteMediator<Int, Anime>() { // 泛型改为单个Anime实体
    
    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, Anime>
    ): MediatorResult {
        val page = when (loadType) {
            LoadType.REFRESH -> 1
            LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
            LoadType.APPEND -> {
                // 从数据库获取最后一条Anime的页码(需要给Anime实体添加currentPage字段)
                val lastPage = database.animeDao().getLastPageNumber()
                if (lastPage == null) {
                    return MediatorResult.Success(endOfPaginationReached = true)
                }
                // 读取存储的分页状态(需单独建表存储hasNextPage信息)
                val hasNextPage = database.paginationDao().getHasNextPage()
                if (!hasNextPage) {
                    return MediatorResult.Success(endOfPaginationReached = true)
                }
                lastPage + 1
            }
        }

        return try {
            val response = apiService.getTopAnime("g", page, PAGE_SIZE_CONFIG)
            database.withTransaction {
                if (loadType == LoadType.REFRESH) {
                    database.animeDao().deleteAllAnime()
                    database.paginationDao().clear()
                }
                // 给每个Anime标记当前页码,用于后续分页判断
                val animeList = response.data.map { it.copy(currentPage = page) }
                database.animeDao().insertAnime(animeList)
                // 存储当前页的分页状态
                database.paginationDao().insert(PaginationInfo(page, response.pagination.hasNextPage))
            }
            MediatorResult.Success(endOfPaginationReached = !response.pagination.hasNextPage)
        } catch (e: Exception) {
            MediatorResult.Error(e)
        } catch (e: HttpException) {
            MediatorResult.Error(e)
        }
    }

    companion object {
        const val PAGE_SIZE_CONFIG = 5
    }
}

3. 修正Compose View的Paging用法

当前View中直接遍历itemSnapshotList.items的写法会绕过Paging的滚动加载检测,导致Paging无法正确判断是否需要加载下一页。正确用法是直接绑定LazyPagingItems:

修正后的LazyVerticalStaggeredGrid:

LazyVerticalStaggeredGrid(
    columns = StaggeredGridCells.Adaptive(200.dp),
    horizontalArrangement = Arrangement.spacedBy(2.dp),
    verticalItemSpacing = 2.dp,
    modifier = modifier.fillMaxSize()
) {
    // 直接使用items绑定LazyPagingItems
    items(topAnimeList) { anime ->
        anime?.let { TopAnimeItem(it) }
    }

    // 添加加载状态提示(可选)
    topAnimeList.apply {
        when (loadState.append) {
            is LoadState.Loading -> {
                item {
                    Box(
                        modifier = Modifier.fillMaxWidth().padding(16.dp),
                        contentAlignment = Alignment.Center
                    ) {
                        CircularProgressIndicator()
                    }
                }
            }
            is LoadState.Error -> {
                item {
                    TextButton(onClick = { retry() }) {
                        Text("加载失败,点击重试")
                    }
                }
            }
            else -> {}
        }
    }
}

4. 其他辅助修正

  • 确保Repository中的PagerpageSize与API每页返回的data数组长度一致(即5)。
  • 保留ViewModel中cachedIn(viewModelScope)的用法,避免重复创建Flow导致的重复请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 18:54:51