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

Kotlin/KMM登录验证:如何在两个协程作用域中捕获异常?

如何在协程中捕获登录凭据无效的异常

核心思路

仓库层抛出的异常会向上传递到ViewModel的协程作用域中,我们可以在ViewModel的协程内通过try-catch捕获异常,再通过状态通知UI层处理;UI层则通过观察ViewModel的状态变量来更新界面(比如隐藏加载、显示错误提示)。


1. 修改ViewModel,捕获异常并更新状态

首先在ViewModel中添加错误状态变量,然后在协程内用try-catch包裹仓库的登录调用,分别处理成功和失败场景:

// 定义状态变量,供UI层观察
private val _loginStatus = MutableStateFlow(false)
val loginStatus: StateFlow<Boolean> = _loginStatus

private val _loginError = MutableStateFlow<String?>(null)
val loginError: StateFlow<String?> = _loginError

fun doLogin(userName: String, password: String) {
    viewModelScope.launch(Dispatchers.IO) {
        try {
            // 调用仓库登录接口,成功则获取User对象
            val user = repo.login(userName, password)
            // 切换到主线程更新成功状态
            withContext(Dispatchers.Main) {
                _loginStatus.value = true
                _loginError.value = null // 清空错误信息
            }
        } catch (e: Exception) {
            // 捕获登录异常(包括凭据无效)
            withContext(Dispatchers.Main) {
                _loginStatus.value = false
                // 根据异常类型设置错误提示,可根据实际情况调整判断逻辑
                _loginError.value = if (e.message?.contains("invalid credentials") == true) {
                    "用户名或密码错误"
                } else {
                    "登录失败,请稍后重试"
                }
            }
        }
    }
}

2. UI层观察状态,处理结果

在登录页面的Composable中,收集ViewModel的状态变量,根据状态更新加载状态和错误提示:

@Composable
fun LoginScreen(loginViewModel: LoginViewModel) {
    val userName = remember { mutableStateOf("") }
    val password = remember { mutableStateOf("") }
    var showProgress by remember { mutableStateOf(false) }
    
    // 收集ViewModel的状态
    val loginError by loginViewModel.loginError.collectAsState()
    val loginStatus by loginViewModel.loginStatus.collectAsState()

    // 监听状态变化,更新加载状态
    LaunchedEffect(loginStatus, loginError) {
        if (loginStatus || loginError != null) {
            showProgress = false
        }
    }

    Column(Modifier.padding(16.dp)) {
        // 用户名、密码输入框实现...
        
        // 显示错误提示
        loginError?.let {
            Text(
                text = it,
                color = Color.Red,
                modifier = Modifier.padding(vertical = 8.dp)
            )
        }

        // 登录按钮
        Button(
            onClick = {
                showProgress = true
                loginViewModel.doLogin(userName.value, password.value)
            },
            enabled = !showProgress
        ) {
            if (showProgress) {
                CircularProgressIndicator(modifier = Modifier.size(20.dp))
            } else {
                Text(text = "登录")
            }
        }
    }
}

进阶优化:自定义异常(可选)

如果需要更精准地区分"凭据无效"和其他异常(比如网络错误),可以在仓库层定义自定义异常:

// 自定义凭据无效异常
class InvalidCredentialsException(message: String = "用户名或密码错误") : Exception(message)

// 仓库层代码修改
suspend fun login(email: String, password: String): User {
    return try {
        appService.login(Credentials.emailPassword(email, password))
    } catch (e: Exception) {
        // 根据底层服务的异常信息判断,抛出自定义异常
        if (e.message?.contains("invalid") == true) {
            throw InvalidCredentialsException()
        } else {
            throw e // 其他异常原样抛出
        }
    }
}

然后ViewModel中可以精准捕获:

catch (e: InvalidCredentialsException) {
    withContext(Dispatchers.Main) {
        _loginError.value = e.message
    }
} catch (e: Exception) {
    withContext(Dispatchers.Main) {
        _loginError.value = "网络异常,请检查网络连接"
    }
}

内容的提问来源于stack exchange,提问作者user14723892

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 21:10:29