Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Exception Kotlin协同程序异常处理-如何抽象try捕获_Exception_Kotlin_Kotlin Coroutines - Fatal编程技术网

Exception Kotlin协同程序异常处理-如何抽象try捕获

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

我试图理解Kotlin协程中的异常处理,所以我提出了一个非常简单的场景,网络调用抛出异常,我的应用程序必须捕获并处理它

如果我用try-catch块包围async.await()调用,它将按预期工作。然而,如果我试图将try-catch抽象到扩展函数中,我的应用程序就会崩溃

我错过了什么

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
    }

    ...
}