异常未被协程捕获
Exception not being caught in Coroutines
我似乎无法在协程中完成错误处理。我已经阅读了很多文章和 exception handling documentation,但我似乎无法正常工作。
这是我的设置:
我的 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
}
}
}
}
我的 UseCase
只处理一些逻辑,在这种情况下是某种验证器
class MyUseCase(private val myRepository: MyRepository) {
suspend operator fun invoke() {
if (checker()) {
throw CustomException("Checker Failed due to: ...")
}
myRepository.doSomething()
}
}
然后我的Repository
只是处理网络层/本地层
object MyRepository {
private val api = ... // Retrofit
suspend fun doSomething() = api.doSomething()
}
这是我的 Retrofit 界面
interface MyInterface {
@POST
suspend fun doSomething()
}
ViewModel
中的 try/catch 可以处理 Retrofit 调用的错误,但是,它无法捕获 UseCase
抛出的 CustomException
中的错误.从我一直在阅读的文章来看,这应该有效。如果我使用 async
我可以做 await
并消耗错误,但在这种情况下我不必使用 async
我一直在思考这个问题。我可能迷路了。
如有任何帮助,我们将不胜感激!提前致谢!
编辑:
这是我收到的错误日志:
com.example.myapp.domain.errors.CustomException
at com.example.myapp.domain.FeatureOne$invoke.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)
错误直接指向显式throw
语句。
尝试使用 CoroutineExceptionHandler
可以解决在协程中处理异常。
CoroutineExceptionHandler context 元素用作协程的通用 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)
在您的 ViewModel
中,确保您的 uiScope
使用 SupervisorJob
而不是 Job
。 SupervisorJob
's 可以单独处理其子项的失败。 Job
会被取消,不像 SupervisorJob
如果您对 AAC 生命周期和 ViewModel 使用 2.1.0
,请改用 viewModelScope
扩展。
据我所知,Retrofit 尚未创建使用 suspend
关键字标记方法的方法。可以参考这个link。
所以你 MyInterface
的正确方法是:
interface MyInterface {
@POST
fun doSomething(): Deferred<Response<YourDataType>>
}
解决此问题的另一种方法是隐藏您的自定义错误对象以实现 CancellationException
例如:
您的 CustomException
可以实现为:
sealed class CustomError : CancellationException() {
data class CustomException(override val message: String = "Checker Failed due to: ...") : CustomError
}
这个异常会在视图模型的 try/catch 块中被捕获
我似乎无法在协程中完成错误处理。我已经阅读了很多文章和 exception handling documentation,但我似乎无法正常工作。
这是我的设置:
我的 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
}
}
}
}
我的 UseCase
只处理一些逻辑,在这种情况下是某种验证器
class MyUseCase(private val myRepository: MyRepository) {
suspend operator fun invoke() {
if (checker()) {
throw CustomException("Checker Failed due to: ...")
}
myRepository.doSomething()
}
}
然后我的Repository
只是处理网络层/本地层
object MyRepository {
private val api = ... // Retrofit
suspend fun doSomething() = api.doSomething()
}
这是我的 Retrofit 界面
interface MyInterface {
@POST
suspend fun doSomething()
}
ViewModel
中的 try/catch 可以处理 Retrofit 调用的错误,但是,它无法捕获 UseCase
抛出的 CustomException
中的错误.从我一直在阅读的文章来看,这应该有效。如果我使用 async
我可以做 await
并消耗错误,但在这种情况下我不必使用 async
我一直在思考这个问题。我可能迷路了。
如有任何帮助,我们将不胜感激!提前致谢!
编辑:
这是我收到的错误日志:
com.example.myapp.domain.errors.CustomException
at com.example.myapp.domain.FeatureOne$invoke.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)
错误直接指向显式throw
语句。
尝试使用 CoroutineExceptionHandler
可以解决在协程中处理异常。
CoroutineExceptionHandler context 元素用作协程的通用 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)
在您的 ViewModel
中,确保您的 uiScope
使用 SupervisorJob
而不是 Job
。 SupervisorJob
's 可以单独处理其子项的失败。 Job
会被取消,不像 SupervisorJob
如果您对 AAC 生命周期和 ViewModel 使用 2.1.0
,请改用 viewModelScope
扩展。
据我所知,Retrofit 尚未创建使用 suspend
关键字标记方法的方法。可以参考这个link。
所以你 MyInterface
的正确方法是:
interface MyInterface {
@POST
fun doSomething(): Deferred<Response<YourDataType>>
}
解决此问题的另一种方法是隐藏您的自定义错误对象以实现 CancellationException
例如:
您的 CustomException
可以实现为:
sealed class CustomError : CancellationException() {
data class CustomException(override val message: String = "Checker Failed due to: ...") : CustomError
}
这个异常会在视图模型的 try/catch 块中被捕获