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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 15:23:11