Kotlin 避免取消子协同程序异常时的父作业

Kotlin 避免取消子协同程序异常时的父作业,kotlin,kotlinx.coroutines,Kotlin,Kotlinx.coroutines,我正在尝试在Android上的Kotlin协同程序中处理异常 我的用例是希望在后台(以异步方式)执行一系列任务,并在单个活动上更新多个UI组件 我设计了一个BaseActivity结构来实现CoroutineScope,这样我就可以将调用的例程与活动的生命周期耦合起来 此外,我还有一个存储库类,用于处理网络调用 我已经实现了同时运行多个任务。我知道如果我使用一个作业对象取消活动的onDestroy()上的所有协程,并在活动中执行(启动)多个协程,则任何单个协程中的异常都将从其协程文本中取消作业。

我正在尝试在Android上的Kotlin协同程序中处理异常

我的用例是希望在后台(以异步方式)执行一系列任务,并在单个活动上更新多个UI组件

我设计了一个
BaseActivity
结构来实现
CoroutineScope
,这样我就可以将调用的例程与活动的生命周期耦合起来

此外,我还有一个
存储库
类,用于处理网络调用

我已经实现了同时运行多个任务。我知道如果我使用一个
作业
对象取消活动的
onDestroy()
上的所有协程,并在活动中执行(
启动
)多个协程,则任何单个协程中的异常都将从其
协程文本
中取消
作业
。由于
作业
附加到活动的生命周期,所以它也将取消所有其他协同程序

我尝试过使用一个
CoroutineExceptionHandler
。它捕获异常,但也取消
作业
。其结果是取消所有其他协同路由

我想要什么

  • 能够使用单个
    作业
    对象与活动生命周期连接
  • 一个协程中的异常不应取消其他协程
  • 在下面添加代码

    class BaseActivity : AppCompatActivity(), CoroutineScope {
    
    val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        launch(coroutineContext) {
            Log.i("GURU", "launch1 -> start")
            val result1Deferred = async { Repository().getData(1) }
            val result2Deferred = async { Repository().getData(2) }
    
            Log.i("GURU", "awaited result1 = " + result1Deferred.await() + result2Deferred.await())
        }
    
    //If Exception is Thrown, Launch1 should still continue to complete
        advancedLaunch(coroutineContext) {
            Log.i("GURU", "launch2 -> start")
            val result1Deferred = async { Repository().getData(3) }
    
            val result2Deferred = async { Repository().getData(4) }
    
            delay(200)
            throw Exception("Exception from launch 2")
    
    
            Log.i("GURU", "awaited result2 = " + result1Deferred.await() + result2Deferred.await())
        }
    
    
    }
    
    
    
    fun CoroutineScope.advancedLaunch(context: CoroutineContext = EmptyCoroutineContext,
                                      exceptionBlock: (Throwable) -> Unit = {Log.i("GURU", it.message)},
                                      launchBlock: suspend CoroutineScope.() -> Unit) {
        val exceptionHandler = CoroutineExceptionHandler { _, throwable -> exceptionBlock(throwable)}
        launch(context + exceptionHandler) { launchBlock() }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
        Log.i("GURU", "job -> cancelled")
    }
    }
    
    此操作的日志结果为

    I/GURU: launch1 -> start
    I/GURU: launch2 -> start
    I/GURU: getData -> start 1
    I/GURU: getData -> start 2
    I/GURU: getData -> start 4
    I/GURU: getData -> start 3
    I/GURU: Exception from launch 2
    
        --------- beginning of crash
    

    您可能希望将
    作业
    替换为


    它防止异常“向上”传播(一个失败的子项不会导致整个作业失败),但仍然允许您“向下”推取消(到正在运行的子项)。

    您可能希望用替换
    作业。@Pawel谢谢。我在文档中错过了它。在正确的答案中加上这个,我会接受的