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

Kotlin使用Paging 3库时PagingSource的load方法早于API返回问题求解

问题根源

你当前的问题本质是异步回调和同步返回的逻辑冲突:
load()是Paging3要求的挂起函数,你调用getFeedsData()触发异步请求之后,没有等待回调结果就直接返回了LoadResult.Page,此时API请求还未完成,feedModel还没有被赋值,甚至会触发空指针异常。
同时你的实现存在架构违规问题:PagingSource属于数据层组件,不应该持有Activity引用、也不应该在内部观察LiveData,会引发内存泄漏。


解决方案

用Kotlin协程的suspendCancellableCoroutine把原有回调式的API请求,转换成挂起函数,让load()函数等待API请求返回结果之后再返回LoadResult。


改造后完整代码
class FeedDatasource : PagingSource<Int, FeedModel.Success_data>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, FeedModel.Success_data> {
        return try {
            val nextPage = params.key ?: 1
            // 挂起等待API请求返回结果,再往下执行
            val feedModel = getFeedsData(nextPage) ?: return LoadResult.Error(NullPointerException("请求结果为空"))
            
            LoadResult.Page(
                data = feedModel.success_data.orEmpty(),
                prevKey = if (nextPage == 1) null else nextPage - 1,
                // 总页数判断逻辑移到这里,没有下一页就返回null
                nextKey = if (nextPage < (feedModel.total_page ?: 0)) nextPage + 1 else null
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    // Paging3要求必须实现的刷新逻辑
    override fun getRefreshKey(state: PagingState<Int, FeedModel.Success_data>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
        }
    }

    // 把回调式API转为挂起函数
    private suspend fun getFeedsData(page: Int): FeedModel? = suspendCancellableCoroutine { continuation ->
        UserRepository.callAPI(
            getJsonObject(page),
            AppConstants.APIMarks.FEED_LIST,
            AppConstants.HTTPMethod.HTTP_POST,
            object : APICallback {
                override fun success(response: MutableLiveData<JsonElement>) {
                    // 直接取LiveData的value即可,不需要观察,回调触发时已经有结果
                    val jsonElement = response.value ?: run {
                        continuation.resume(null)
                        return
                    }
                    val model = Gson().fromJson(jsonElement, FeedModel::class.java)
                    continuation.resume(model)
                }

                override fun failure() {
                    continuation.resumeWithException(RuntimeException("接口请求失败"))
                }
            }
        )
        // 协程取消时可以在这里添加取消网络请求的逻辑
        continuation.invokeOnCancellation { }
    }
}

// 原有接口不需要改动
interface APICallback {
    fun success(response: MutableLiveData<JsonElement>)
    fun failure()
}

注意事项
  • 不要在PagingSource中持有Activity、Fragment等视图层实例,也不要在PagingSource中调用LiveData的observe方法,避免内存泄漏
  • 如果你的网络库本身支持协程(比如Retrofit的 suspend 适配器),可以直接返回挂起结果,不需要额外用suspendCancellableCoroutine转换
  • nextKey不要固定返回nextPage +1,要判断是否已经到总页数,没有更多数据时返回null,通知Paging3加载结束

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 01:06:03