Избегайте отмены родительской работы в случае исключения из-за детской сопрограммы
Я экспериментирую с обработкой исключений в сопрограммах Kotlin на Android.
Мой вариант использования - я хочу выполнить кучу задач в фоновом режиме (асинхронно) и обновить несколько компонентов пользовательского интерфейса в одном действии.
Я разработал BaseActivity
структура для реализации CoroutineScope
так что я могу соединить процедуры с жизненным циклом деятельности.
Кроме того, у меня есть 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
1 ответ
Вы можете заменить свой Job
с SupervisorJob
,
Он предотвращает распространение исключений "вверх" (один отказавший дочерний элемент не приведет к сбою всей работы), но все же позволяет отменить отмену "вниз" (для работающих потомков).