Избегайте отмены родительской работы в случае исключения на детскую сопрограмму - PullRequest
0 голосов
/ 03 ноября 2018

Я экспериментирую с обработкой исключений в сопрограммах Kotlin на Android .

Мой вариант использования: я хочу выполнить кучу задач в фоновом режиме (асинхронно) и обновить несколько компонентов пользовательского интерфейса за одно действие.

Я разработал структуру BaseActivity для реализации CoroutineScope, чтобы я мог соединить процедуры, вызванные жизненным циклом действия.

Также у меня есть класс Repository, который обрабатывает сетевые вызовы.

Я выполнил несколько задач одновременно. Я знаю, что если я использую один Job объект для отмены всех сопрограмм в onDestroy() действия и выполнения (launch) нескольких сопрограмм в действии, исключение в любой отдельной сопрограмме отменяет Job из его CoroutineContext , А поскольку Job привязан к жизненному циклу активности, он отменяет также все другие сопрограммы.

Я пытался использовать CoroutineExceptionHandler. Это ловит исключение, но отменяет Job тоже. Что в результате отменяет все другие сопрограммы.

Что я хочу?

  1. Возможность использовать один Job объект для прикрепления с жизненным циклом активности
  2. Исключение в одной сопрограмме не должно отменять другие сопрограммы

Добавление кода ниже

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

1 Ответ

0 голосов
/ 03 ноября 2018

Вы можете заменить Job на SupervisorJob.

Он предотвращает распространение исключений «вверх» (один отказавший дочерний элемент не приведет к сбою всей работы), но все же позволяет отменить отмену «вниз» (для работающих потомков).

...