ViewModel中协程与Ktor Client接口调用异常致流程停滞的解决方法
问题解决:ViewModel协程中调用Ktor接口后代码停滞的问题
问题根源
Ktor Client默认在请求失败(比如HTTP错误状态码、网络异常、请求超时等情况)时会抛出异常。你当前的代码通过CoroutineExceptionHandler捕获了异常,但协程在抛出未处理的异常后会直接终止,导致后续的_destinationRoute.emit代码完全不会执行。
你的signIn方法仅处理了2xx状态码的情况,但如果请求过程中抛出异常(比如401未授权、500服务器错误、无网络),异常会直接向上传递,触发CoroutineExceptionHandler后协程就停了。
解决方案
有两种可行的修改方式,核心都是避免协程因异常终止,确保后续代码能正常执行:
方案1:在signIn方法内捕获异常,统一返回布尔值
把异常处理封装在Dao层,对外仅返回登录是否成功的布尔值,上层逻辑无需关心异常细节:
suspend fun signIn(requestModel: SignInRequestModel): Boolean { return try { val response = httpClient.post( urlString = "http://someHost:8080/api/v1/user/signIn", block = { setBody(requestModel) } ) response.status.value in 200..299 } catch (e: Exception) { Log.e("SignInError", "登录请求失败: ${e.message}") false // 任何异常都视为登录失败 } }
方案2:在ViewModel的协程内局部捕获异常
如果不想修改Dao层,可以在调用signIn的地方用try-catch包裹,处理异常后继续执行后续的路由跳转逻辑:
private fun chooseDestination() = viewModelScope.launch(Dispatchers.IO) { val password = dataStoreRepository.readPassword() val phoneNumber = dataStoreRepository.readPhoneNumber() if (password == null || phoneNumber == null) { _destinationRoute.emit(RouteBuilder.signInScreen()) return@launch } val isUserAuthenticated = try { authDao.signIn( requestModel = SignInRequestModel( phone = phoneNumber, password = password ) ) } catch (e: Exception) { Log.i("SplashException", e.toString()) false // 异常场景下默认视为未认证 } _destinationRoute.emit( if (isUserAuthenticated) RouteBuilder.homeScreen() else RouteBuilder.signInScreen() ) }
补充说明
如果你仍然需要全局的CoroutineExceptionHandler处理其他未预期的异常,建议保留,但要明确:CoroutineExceptionHandler是协程的最后异常处理防线,一旦触发,协程会立即终止。所以需要继续执行后续代码的场景,一定要用局部try-catch处理特定异常。
内容的提问来源于stack exchange,提问作者MohammadBaqer
相关产品推荐
相关产品推荐

