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

如何管理多类型错误状态?区分全屏错误与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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 05:48:06