我正在尝试使用 在 Android 上的 Kotlin 协程中处理异常。

我的用例是我想在后台(以异步方式)执行一堆任务并在单个事件上更新多个 UI 组件。

我设计了一个 BaseActivity 结构来实现 CoroutineScope,这样我就可以将调用的 couroutines 与事件的生命周期结合起来。

另外,我有一个处理网络调用的 Repository 类。

我已经实现了同时运行多个任务。我知道如果我使用单个 Job 对象来取消事件的 onDestroy() 上的所有协程并在事件中执行( launch )多个协程,则任何单个协程中的异常都会从其 Job 中取消 CoroutineContext 。由于 Job 附加到事件的生命周期,它也会取消所有其他协程。

我试过使用 CoroutineExceptionHandler 。它捕获异常但也取消了 Job。结果取消了所有其他协程。

我想要的是?

  • 能够使用 单个 Job 对象来附加事件生命周期
  • 一个协程中的异常 不应该 取消其他协程

  • 在下面添加代码
    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
    

    最佳答案

    您可能想用 Job 替换 SupervisorJob

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

    关于kotlin - 避免在子协程异常时取消父作业,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53132989/

    10-14 05:02