Jetpack Compose配合Paging3时RemoteMediator错误如何传递到ViewModel
你当前的写法无法捕获错误核心原因是:fetchPeople()方法仅构造并返回Pager的Flow对象,不会立刻执行网络请求,实际加载逻辑是在PagingData被订阅时才触发的,所以你在ViewModel的init块里加的try-catch只能捕获构造Flow过程中出现的异常,完全接触不到RemoteMediator加载时抛出的错误。
以下是两种常用的实现方案:
方案1:通过Paging3原生加载状态监听处理(推荐)
Paging3本身提供了加载状态回调,包含了refresh、prepend、append三个阶段的状态,RemoteMediator返回的错误会自动封装到加载状态里,你可以直接在UI层或者ViewModel层监听处理。
调整ViewModel代码
@ExperimentalPagingApi @HiltViewModel class PeopleViewModel @Inject constructor(private val repo: PeopleRepository) : ViewModel() { // 直接返回PagingData Flow,用cachedIn做页面重建缓存 val peopleFlow = repo.fetchPeople() .cachedIn(viewModelScope) }
Compose层收集并处理错误
val lazyPagingItems = viewModel.peopleFlow.collectAsLazyPagingItems() when { // 处理首次刷新/下拉刷新的错误 lazyPagingItems.loadState.refresh is LoadState.Error -> { val error = (lazyPagingItems.loadState.refresh as LoadState.Error).error // 展示错误UI,比如错误页、吐司提示 Text(text = error.message ?: "数据加载失败") } // 处理加载中状态 lazyPagingItems.loadState.refresh is LoadState.Loading -> { CircularProgressIndicator() } else -> { LazyColumn { items(lazyPagingItems) { character -> // 渲染列表项 } // 处理加载更多的错误和状态 lazyPagingItems.loadState.append.apply { when(this) { is LoadState.Loading -> item { CircularProgressIndicator() } is LoadState.Error -> item { Text("加载更多失败:${error.message}") } else -> {} } } } } }
方案2:ViewModel层统一接收错误
如果你需要把错误统一收拢到ViewModel处理,可以通过事件流传递RemoteMediator捕获的错误。
1. 新增全局错误事件流
在Hilt模块中提供单例的错误事件流:
@Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun provideErrorEventFlow(): MutableSharedFlow<Throwable> { return MutableSharedFlow(extraBufferCapacity = 1) } }
2. 修改RemoteMediator发送错误
@ExperimentalPagingApi class PeopleRemoteMediator @Inject constructor( private val database: AppDatabase, private val service: PeopleService, private val errorFlow: MutableSharedFlow<Throwable> ) : RemoteMediator<Int, SWCharacter>() { // 省略其他不变的代码 override suspend fun load(loadType: LoadType, state: PagingState<Int, SWCharacter>): MediatorResult { return try { // 省略原有请求逻辑 } catch (e: IOException) { errorFlow.emit(e) MediatorResult.Error(e) } catch (e: ResponseException) { errorFlow.emit(e) MediatorResult.Error(e) } } }
3. 调整Repository暴露错误流
class PeopleRepository @Inject constructor( private val database: AppDatabase, private val service: PeopleService, private val errorFlow: MutableSharedFlow<Throwable> ) { val globalErrorFlow: Flow<Throwable> = errorFlow @ExperimentalPagingApi fun fetchPeople(): Flow<PagingData<SWCharacter>> { return Pager( PagingConfig(pageSize = 10, enablePlaceholders = false, prefetchDistance = 3), remoteMediator = PeopleRemoteMediator(database, service, errorFlow), pagingSourceFactory = { database.people().pagingSource() } ).flow } }
4. ViewModel收集错误更新UI
@ExperimentalPagingApi @HiltViewModel class PeopleViewModel @Inject constructor(private val repo: PeopleRepository) : ViewModel() { private val _uiState = mutableStateOf<UIState>(UIState.Loading) val uiState: State<UIState> = _uiState val peopleFlow = repo.fetchPeople().cachedIn(viewModelScope) init { // 收集错误事件更新UI状态 viewModelScope.launch { repo.globalErrorFlow.collect { e -> _uiState.value = UIState.Error(message = e.message ?: "加载失败") } } // 数据加载成功更新UI状态 viewModelScope.launch { peopleFlow.collectLatest { _uiState.value = UIState.Success(data = it) } } } }
内容的提问来源于stack exchange,提问作者Valerio
相关产品推荐
相关产品推荐

