使用Result对象抛出异常,在ViewModel中无法捕获的问题
使用Result返回失败结果后,ViewModel无法触发onFailure回调
问题描述
在使用Kotlin Result 对象封装失败结果时,发现ViewModel中无法捕获到UseCase返回的失败状态,onFailure 回调始终不执行。
相关代码
Action.kt
abstract class Action<in Input, out Output> { protected abstract suspend fun action(input: Input): Output protected open fun dispatcher(): CoroutineDispatcher = Dispatchers.IO suspend operator fun invoke(input: Input): Output = withContext(dispatcher()) { action(input) } }
IsNewDataToBeFetchedFromServerUseCase.kt
class IsNewDataToBeFetchedFromServerUseCase @Inject constructor( @IoDispatcher private val dispatcher: CoroutineDispatcher, ) : Action<Unit, Result<Boolean>>() { override suspend fun action(input: Unit): Result<Boolean> = withContext(context = dispatcher) { return@withContext Result.failure(Exception("Message")) } }
ViewModel中的调用代码
useCases.isNewDataToBeFetchedFromServerUseCase.invoke(Unit).onSuccess { println("成功回调") }.onFailure { println(it.message) // 此处未执行 }
问题原因
- 协程作用域缺失:
invoke是挂起函数,必须在协程上下文内调用。直接在ViewModel中调用挂起函数会导致代码无法正常执行,无法正确获取到Result对象。 - 冗余的协程调度器切换:
Action类的invoke方法已经通过withContext(dispatcher())切换了调度器,UseCase中重复调用withContext属于多余操作,不会导致问题,但会增加不必要的协程切换开销。
解决方案
1. 在ViewModel的协程作用域内调用挂起函数
使用viewModelScope(AndroidX Lifecycle库提供)启动协程,确保在正确的生命周期内执行挂起逻辑:
viewModelScope.launch { useCases.isNewDataToBeFetchedFromServerUseCase.invoke(Unit).onSuccess { println("成功回调") }.onFailure { println(it.message) // 现在可以正常触发 } }
2. 移除UseCase中的冗余协程切换
修改UseCase代码,复用Action类的调度器逻辑:
class IsNewDataToBeFetchedFromServerUseCase @Inject constructor( @IoDispatcher private val dispatcher: CoroutineDispatcher, ) : Action<Unit, Result<Boolean>>() { override fun dispatcher(): CoroutineDispatcher = dispatcher override suspend fun action(input: Unit): Result<Boolean> { return Result.failure(Exception("Message")) } }
补充说明
Kotlin Result 是用于封装成功/失败结果的容器,它不会主动抛出异常,仅将失败状态包装在对象内。只有调用Result.getOrThrow()时才会抛出封装的异常。因此只要正确获取到Result对象,onFailure回调就会被触发,核心问题是协程作用域的缺失。
内容的提问来源于stack exchange,提问作者Devrath
相关产品推荐
相关产品推荐

