Exception Kotlin协同程序异常处理-如何抽象try捕获
我试图理解Kotlin协程中的异常处理,所以我提出了一个非常简单的场景,网络调用抛出异常,我的应用程序必须捕获并处理它 如果我用try-catch块包围async.await()调用,它将按预期工作。然而,如果我试图将try-catch抽象到扩展函数中,我的应用程序就会崩溃 我错过了什么Exception Kotlin协同程序异常处理-如何抽象try捕获,exception,kotlin,kotlin-coroutines,Exception,Kotlin,Kotlin Coroutines,我试图理解Kotlin协程中的异常处理,所以我提出了一个非常简单的场景,网络调用抛出异常,我的应用程序必须捕获并处理它 如果我用try-catch块包围async.await()调用,它将按预期工作。然而,如果我试图将try-catch抽象到扩展函数中,我的应用程序就会崩溃 我错过了什么 import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import kotlinx.coroutines.* cla
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
class Main2Activity : AppCompatActivity() {
private val job: Job = Job()
private val scope = CoroutineScope(Dispatchers.Default + job)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
runCode()
}
private suspend fun asyncCallThrowsException(): Deferred<Boolean> =
withContext(Dispatchers.IO) {
Thread.sleep(3000)// simulates a blocking request/response (not on the Main thread, though)
throw(Exception())
}
suspend fun <T> Deferred<T>.awaitAndCatch() {
try {
this.await()
} catch (e: Exception) {
println("exception caught inside awaitAndCatch")
}
}
private fun runCode() {
scope.launch {
//This block catches the exception.
try {
val resultDeferred = asyncCallThrowsException()
resultDeferred.await()
} catch (e: Exception) {
println("exception caught inside try-catch")
}
//This line does not, and crashes my app.
asyncCallThrowsException().awaitAndCatch()
}
}
}
问题与如何捕获异常无关。问题是,当异步作业失败(抛出异常)时,它会取消为活动所做的作业 即使代码可以捕获异常并打印消息,父作业也会尽快终止 不要这样做:
val:Job=Job()
,尝试val:Job=SupervisorJob()
主管作业不会在其子作业失败时取消,因此不会使应用程序崩溃
或者,如果希望找到一种启动异步作业的方法,而该异步作业没有此问题,请参阅:问题与捕获异常的方式无关。问题是,当异步作业失败(抛出异常)时,它会取消为活动所做的作业 即使代码可以捕获异常并打印消息,父作业也会尽快终止 不要这样做:
val:Job=Job()
,尝试val:Job=SupervisorJob()
主管作业不会在其子作业失败时取消,因此不会使应用程序崩溃
或者,如果您希望找到一种启动异步作业的方法,而该异步作业没有此问题,请参阅:要获得正确的解决方案,需要解决的问题是使其与结构化并发的原则兼容 您使用
async
的动机到底是什么?在启动async
和等待它之间,您计划同时做什么
如果async
启动和wait
调用都是单个工作单元的一部分,并且async
调用的成功是整体成功的先决条件,那么将整个工作单元包装在coroutineScope
中
如果您希望在后台启动此任务,并等待稍后调用的Android回调执行此任务,则无法将其封装到单个工作单元中。您应该将异步任务附加到顶级的CoroutineScope
,其中应该有一个SupervisorJob
正确的方法如下所示:
Kotlin标准库添加了
MainScope()
委托作为一种方便,这样您就不会出错。要获得正确的解决方案,需要解决的问题是使其与结构化并发原则兼容
您使用async
的动机到底是什么?在启动async
和等待它之间,您计划同时做什么
如果async
启动和wait
调用都是单个工作单元的一部分,并且async
调用的成功是整体成功的先决条件,那么将整个工作单元包装在coroutineScope
中
如果您希望在后台启动此任务,并等待稍后调用的Android回调执行此任务,则无法将其封装到单个工作单元中。您应该将异步任务附加到顶级的CoroutineScope
,其中应该有一个SupervisorJob
正确的方法如下所示:
Kotlin标准库添加了
MainScope()
委托作为一种方便,这样您就不会出错。我将其更改为SupervisorJob
,但应用程序还是崩溃了。我必须向作用域传递一个CoroutineExceptionHandler
,以防止应用程序崩溃。我认为我的扩展函数的try-catch
如果作用域有一个SupervisorJob
…ps:使用CoroutineExceptionHandler
,Job
和SupervisorJob
的行为相同-应用程序不会崩溃。我将其更改为SupervisorJob
,但应用程序还是崩溃了。我必须向作用域传递一个CoroutineExceptionHandler
,以防止应用程序崩溃。我认为如果作用域有一个SupervisorJob
…ps,CoroutineExceptionHandler
,我的扩展函数的try-catch
就足够了。应用程序不会崩溃。
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
class Main4Activity : AppCompatActivity() {
private val job: Job = Job()
private val scope = CoroutineScope(Dispatchers.Default + job)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runCode()
}
private suspend fun callThrowsException(): String =
withContext(Dispatchers.IO) {
Thread.sleep(3000)// simulates a blocking request/response (not on the Main thread, though)
throw(Exception())
"my result"
}
suspend fun <T> Deferred<T>.awaitAndCatch(): T? {
try {
return this.await()
} catch (e: Exception) {
println("exception caught inside awaitAndCatch")
}
return null
}
private fun runCode() {
scope.launch {
val resultDeferred: Deferred<String> = async { callThrowsException() }
var result: String?
// This doesn't catch the throwable, and my app crashes - but the message gets printed to the console.
try {
result = resultDeferred.await()
} catch (e: Exception) {
println("exception caught inside try-catch")
}
// This doesn't catch the throwable, and my app crashes - but the message gets printed to the console.
result = resultDeferred.awaitAndCatch()
}
}
}
class MyActivity : AppCompatActivity(), CoroutineScope by MainScope() {
override fun onDestroy() {
cancel() // cancel is extension on CoroutineScope
}
...
}