网络响应处理异常:Error状态无法触发问题求助
问题:Paging3网络请求Error状态无法触发
Loading和Success状态可正常工作,但Error状态无法触发。模拟错误场景时,触发的是Loading和Success状态,而非Error状态。
相关代码如下:
Repository类
fun getMoviesStream(genreId: String?, sortBy: String): Flow<PagingData<Result>> { return Pager( config = PagingConfig(pageSize = MOVIES_PAGE_SIZE, enablePlaceholders = false), pagingSourceFactory = { MoviesPagingSource(api, genreId, sortBy) } ).flow }
ViewModel类
private val _moviesResponse = MutableStateFlow<Resource<PagingData<Result>>>(Resource.Loading()) val moviesResponse = _moviesResponse.asStateFlow() fun getMovies(genreId: String?, sortBy: String) { viewModelScope.launch { _moviesResponse.value = Resource.Loading() moviesRepository.getMoviesStream(genreId, sortBy).cachedIn(viewModelScope) .catch { throwable -> _moviesResponse.value = Resource.Error( throwable.localizedMessage ?: "An unexpected error occurred" ) } .collect { result -> delay(500) _moviesResponse.value = Resource.Success(result) } } }
Fragment类
private fun requestMoviesData(genreId: String?, sortBy: String) { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { moviesViewModel.getMovies(genreId, sortBy) moviesViewModel.moviesResponse.collect { response -> when (response) { is Resource.Error -> { Toast.makeText( requireContext(), response.message, Toast.LENGTH_SHORT ).show() } is Resource.Loading -> { binding.spinnerLoading.visibility = View.VISIBLE } is Resource.Success -> { binding.spinnerLoading.visibility = View.GONE response.data?.let { moviesAdapter.submitData(it) } } } } } } }
问题原因
- Paging3的Flow特性:
Pager.flow不会直接抛出异常,网络错误会被封装到PagingData的LoadState中,不会触发catch操作符。 - 逻辑覆盖:当前
collect块会在任何情况下(包括错误场景返回空PagingData)执行,覆盖了catch中设置的Error状态。 cachedIn位置错误:放在catch之后会导致异常被缓存流程吞掉,无法被捕获。
修复方案
1. 修改ViewModel的状态处理逻辑
监听PagingData的loadStateFlow来捕获错误,替换原有的catch逻辑:
private val _moviesResponse = MutableStateFlow<Resource<PagingData<Result>>>(Resource.Loading()) val moviesResponse = _moviesResponse.asStateFlow() fun getMovies(genreId: String?, sortBy: String) { viewModelScope.launch { _moviesResponse.value = Resource.Loading() moviesRepository.getMoviesStream(genreId, sortBy) .cachedIn(viewModelScope) .collectLatest { pagingData -> _moviesResponse.value = Resource.Success(pagingData) // 监听加载状态,捕获错误 pagingData.loadStateFlow.collect { loadStates -> val refreshError = loadStates.refresh as? LoadState.Error refreshError?.let { _moviesResponse.value = Resource.Error( it.error.localizedMessage ?: "An unexpected error occurred" ) } } } } }
2. 确保PagingSource正确抛出异常
检查MoviesPagingSource的load方法,网络请求失败时必须返回LoadResult.Error,而非空数据:
class MoviesPagingSource(private val api: MovieApi, ...) : PagingSource<Int, Result>() { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Result> { return try { val currentPage = params.key ?: 1 val response = api.getMovies(genreId, sortBy, currentPage) if (!response.isSuccessful) { throw IOException("Request failed with code ${response.code()}") } val movies = response.body()?.results ?: emptyList() LoadResult.Page( data = movies, prevKey = if (currentPage == 1) null else currentPage - 1, nextKey = if (movies.isEmpty()) null else currentPage + 1 ) } catch (e: Exception) { // 将异常封装为LoadResult.Error LoadResult.Error(e) } } }
3. 可选:优化Fragment的分页错误处理
如果需要处理分页加载时的错误,可以监听Adapter的loadStateFlow:
private fun requestMoviesData(genreId: String?, sortBy: String) { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { moviesViewModel.getMovies(genreId, sortBy) moviesViewModel.moviesResponse.collect { response -> when (response) { is Resource.Error -> { Toast.makeText(requireContext(), response.message, Toast.LENGTH_SHORT).show() binding.spinnerLoading.visibility = View.GONE } is Resource.Loading -> { binding.spinnerLoading.visibility = View.VISIBLE } is Resource.Success -> { binding.spinnerLoading.visibility = View.GONE response.data?.let { moviesAdapter.submitData(it) } // 监听分页加载错误 moviesAdapter.loadStateFlow.collect { loadStates -> val appendError = loadStates.append as? LoadState.Error appendError?.let { Toast.makeText(requireContext(), it.error.localizedMessage, Toast.LENGTH_SHORT).show() } } } } } } } }
内容的提问来源于stack exchange,提问作者Anubis
相关产品推荐
相关产品推荐

