如何管理多类型错误状态?区分全屏错误与Toast提示方案
解决方案:区分错误类型,让UI层按需处理
是的,你需要在状态类中新增区分错误类型的字段(或者用密封类封装错误),这样才能让UI层明确知道该用全屏提示还是Toast展示错误,同时避免把Context传入ViewModel。
下面提供两种可行的实现方案:
方案1:用密封类封装不同类型的错误
这种方式更灵活,适合未来可能扩展错误类型的场景:
1. 定义错误类型密封类
sealed class UiError { // 重大错误,需全屏提示 data class FullScreen(val throwable: Throwable) : UiError() // 次要错误,需Toast提示 data class Toast(val throwable: Throwable) : UiError() }
2. 更新ServerState状态类
替换原来的单一error字段为封装后的uiError:
data class ServerState( val isLoading: Boolean = true, val fileData: MainData? = null, val uiError: UiError? = null )
3. 修改ViewModel中的错误处理逻辑
新增对应错误类型的发射方法,替换原有的emitException:
private val _uiState = MutableStateFlow(ServerState()) val uiState = _uiState.asStateFlow() fun addFile(bytes: ByteArray) { viewModelScope.launch { runCatching { client.add { rawFile["file_test"] = bytes } }.onFailure { e -> emitToastError(e) } } } // 发射全屏错误(比如客户端连接失败场景) private fun emitFullScreenError(e: Throwable) { val mappedError = ExceptionHandler.mapException(e) _uiState.update { it.copy(uiError = UiError.FullScreen(mappedError)) } } // 发射Toast错误(比如单个POST方法异常) private fun emitToastError(e: Throwable) { val mappedError = ExceptionHandler.mapException(e) _uiState.update { it.copy(uiError = UiError.Toast(mappedError)) } } // 清除错误状态,避免重复提示 fun clearError() { _uiState.update { it.copy(uiError = null) } }
4. UI层处理错误
在UI层(比如Activity/Fragment/Composable)收集状态,根据错误类型处理:
// 以Compose为例 LaunchedEffect(uiState.uiError) { when (val error = uiState.uiError) { is UiError.FullScreen -> { // 展示全屏错误UI,比如替换当前内容的错误页 } is UiError.Toast -> { // 用UI层的Context显示Toast,无需传入ViewModel Toast.makeText(context, error.throwable.message ?: "操作失败", Toast.LENGTH_SHORT).show() // 显示后清除错误状态 viewModel.clearError() } null -> {} } }
方案2:拆分错误字段(更简洁)
如果错误类型固定只有两种,直接在状态类中拆分两个错误字段更直观:
1. 更新ServerState状态类
data class ServerState( val isLoading: Boolean = true, val fileData: MainData? = null, val fullScreenError: Throwable? = null, val toastError: Throwable? = null )
2. 修改ViewModel逻辑
private val _uiState = MutableStateFlow(ServerState()) val uiState = _uiState.asStateFlow() fun addFile(bytes: ByteArray) { viewModelScope.launch { runCatching { client.add { rawFile["file_test"] = bytes } }.onFailure { e -> val mappedError = ExceptionHandler.mapException(e) _uiState.update { it.copy(toastError = mappedError) } } } } // 处理连接失败等重大错误 fun handleConnectionError(e: Throwable) { val mappedError = ExceptionHandler.mapException(e) _uiState.update { it.copy(fullScreenError = mappedError, toastError = null) } } // 清除Toast错误状态 fun clearToastError() { _uiState.update { it.copy(toastError = null) } } // 清除全屏错误状态(比如用户点击重试后) fun clearFullScreenError() { _uiState.update { it.copy(fullScreenError = null) } }
3. UI层处理
// 监听全屏错误 LaunchedEffect(uiState.fullScreenError) { uiState.fullScreenError?.let { // 展示全屏错误页 } } // 监听Toast错误 LaunchedEffect(uiState.toastError) { uiState.toastError?.let { Toast.makeText(context, it.message ?: "操作失败", Toast.LENGTH_SHORT).show() viewModel.clearToastError() } }
核心思路
- ViewModel只负责通知错误类型,不处理具体的UI展示逻辑(比如Toast),避免持有Context。
- 通过状态类区分错误类型,让UI层自主决定展示方式,符合MVVM的职责分离原则。
- 错误状态使用后要及时清除,防止重复触发提示。
内容的提问来源于stack exchange,提问作者Yash Garg
相关产品推荐
相关产品推荐

