协程中未捕获异常

Kur*_*sta 9 android kotlin kotlin-coroutines

我似乎无法在协程中完成错误处理。我一直在阅读很多文章和异常处理文档,但我似乎无法让它工作。

这是我的设置:

ViewModel用它的范围启动协程

class MyViewModel(private var myUseCase: MyUseCase) : ViewModel() {
    private val viewModelJob = Job()
    private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)

    fun doSomething() {
        uiScope.launch {
            try {
                myUseCase()
            } catch (exception: Exception) {
                // Do error handling here
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

UseCase只处理一些逻辑,在这种情况下是某种验证器

class MyUseCase(private val myRepository: MyRepository) {
    suspend operator fun invoke() {
        if (checker()) {
            throw CustomException("Checker Failed due to: ...")
        }

        myRepository.doSomething()
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我Repository只是处理网络层/本地层

object MyRepository {
    private val api = ... // Retrofit

    suspend fun doSomething() = api.doSomething()
}
Run Code Online (Sandbox Code Playgroud)

这是我的改造界面

interface MyInterface {
    @POST
    suspend fun doSomething()
}
Run Code Online (Sandbox Code Playgroud)

从该try / catch语句ViewModel但是可以处理从改造调用的错误,它不能从捕获错误CustomException被抛出UseCase。从我一直在阅读的文章来看,这应该有效。如果我使用,async我可以做await并消耗错误,但async在这种情况下我不必使用,我一直在思考这个问题。我可能会迷路。

任何帮助将不胜感激!提前致谢!

编辑:

这是我收到的错误日志:

com.example.myapp.domain.errors.CustomException
        at com.example.myapp.domain.FeatureOne$invoke$2.invokeSuspend(FeatureOne.kt:34)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
Run Code Online (Sandbox Code Playgroud)

错误直接指向显式throw语句。

Jee*_*ede 5

尝试使用CoroutineExceptionHandler可以是处理协程内异常的解决方法。

CoroutineExceptionHandler 上下文元素用作协程的通用 catch 块,其中可能会发生自定义日志记录或异常处理。它类似于使用Thread.uncaughtExceptionHandler.

如何使用它?

val handler = CoroutineExceptionHandler { _, exception -> 
    println("Caught $exception") 
}
val job = GlobalScope.launch(handler) {
    throw AssertionError()
}
val deferred = GlobalScope.async(handler) {
    throw ArithmeticException() // Nothing will be printed, relying on user to call 
    deferred.await()
}
joinAll(job, deferred)
Run Code Online (Sandbox Code Playgroud)

在您的中ViewModel,请确保您uiScope正在使用SupervisorJob而不是Job. SupervisorJob可以单独处理其子级的失败。Job会被取消,不像SupervisorJob

如果您使用2.1.0AAC Lifecycle 和 ViewModel,请改用viewModelScope扩展。

  • 仅供参考,“挂起乐趣”周围的“try-catch”应该以自然的方式工作,并且会捕获被调用代码中的任何异常,即使在协程挂起并恢复之后,这仍然可以工作。OP 的失败很可能是由于“async-await”使用不当造成的。如果“async”任务抛出异常,则会取消“async”协程,并将取消传播到父协程。`SupervisorJob` 忽略取消,但这是一种解决方法,而不是解决方案。 (2认同)