Android数据层通过异常向UI层暴露错误:Jetpack Compose处理方案及示例问询
Android数据层异常处理在Jetpack Compose中的实现
官方推荐用异常而非Result<T>处理数据层错误,核心逻辑是:数据层抛出异常,ViewModel层(或Flow操作环节)捕获异常并转换为UI可识别的状态,UI层仅根据状态渲染对应界面。以下是具体实现方案和完整示例:
一、挂起函数的异常处理(ViewModel + Compose)
1. 定义UI状态密封类
用密封类封装加载、成功、错误三种核心UI状态:
sealed interface PlantUiState { object Loading : PlantUiState data class Success(val plants: List<Plant>) : PlantUiState data class Error(val message: String) : PlantUiState }
2. ViewModel层捕获异常
在ViewModel中调用数据层挂起函数,通过try/catch捕获异常并更新UI状态:
class PlantViewModel(private val plantRepository: PlantRepository) : ViewModel() { private val _uiState = MutableStateFlow<PlantUiState>(PlantUiState.Loading) val uiState: StateFlow<PlantUiState> = _uiState.asStateFlow() fun fetchPlants() { viewModelScope.launch { _uiState.value = PlantUiState.Loading try { // 调用数据层挂起函数,可能抛出网络/数据异常 val plants = plantRepository.getPlants() _uiState.value = PlantUiState.Success(plants) } catch (e: NetworkUnavailableException) { _uiState.value = PlantUiState.Error("网络不可用,请检查网络设置") } catch (e: ApiResponseException) { _uiState.value = PlantUiState.Error("服务器错误:${e.errorCode}") } catch (e: Exception) { _uiState.value = PlantUiState.Error("加载失败,请重试") } } } }
3. Compose UI层渲染状态
在Compose中收集ViewModel的状态,根据状态渲染对应UI:
@Composable fun PlantScreen(viewModel: PlantViewModel = viewModel()) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() Box(modifier = Modifier.fillMaxSize()) { when (uiState) { PlantUiState.Loading -> { CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) } is PlantUiState.Success -> { LazyColumn(modifier = Modifier.fillMaxSize()) { items((uiState as PlantUiState.Success).plants) { plant -> PlantItem(plant) } } } is PlantUiState.Error -> { Column( modifier = Modifier.align(Alignment.Center), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp) ) { Text( text = (uiState as PlantUiState.Error).message, color = MaterialTheme.colorScheme.error ) Button(onClick = { viewModel.fetchPlants() }) { Text("重试") } } } } } }
二、Flow流的异常处理
如果数据层返回Flow(比如结合本地缓存和远程数据的场景),可通过Flow的catch操作符捕获异常:
1. 数据层Repository
数据层的Flow不处理异常,直接抛出:
class PlantRepository( private val plantApi: PlantApi, private val plantDao: PlantDao ) { fun getPlantsFlow(): Flow<List<Plant>> = flow { // 先返回本地缓存数据 emit(plantDao.getAllPlants()) // 请求远程数据,失败则抛出异常 val remotePlants = plantApi.getPlants() plantDao.insertAll(remotePlants) // 返回更新后的本地数据 emit(plantDao.getAllPlants()) } }
2. ViewModel层捕获异常
在ViewModel收集Flow时,用catch捕获异常并更新UI状态:
class PlantViewModel(private val plantRepository: PlantRepository) : ViewModel() { private val _uiState = MutableStateFlow<PlantUiState>(PlantUiState.Loading) val uiState: StateFlow<PlantUiState> = _uiState.asStateFlow() init { observePlants() } private fun observePlants() { viewModelScope.launch { plantRepository.getPlantsFlow() .catch { e -> // 根据异常类型返回对应错误提示 val errorMsg = when (e) { is SocketTimeoutException -> "请求超时,请重试" is IOException -> "网络连接失败" else -> "数据加载失败" } _uiState.value = PlantUiState.Error(errorMsg) } .collect { plants -> _uiState.value = PlantUiState.Success(plants) } } } }
UI层渲染逻辑与挂起函数场景完全一致,无需修改。
三、核心注意事项
- 自定义异常类:建议针对业务场景定义特定异常(如
NetworkUnavailableException、ApiResponseException),可更精准地处理不同错误场景,给用户更友好的提示。 - 避免UI层处理异常:Composable函数应专注于UI渲染,异常处理逻辑放在ViewModel或数据层的Flow操作中,保持UI层简洁可维护。
- 状态驱动UI:无论用挂起函数还是Flow,最终都通过更新UI状态驱动界面变化,符合Jetpack Compose的状态驱动设计理念。
内容的提问来源于stack exchange,提问作者me.at.coding
相关产品推荐
相关产品推荐

