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
相关产品推荐
相关产品推荐

