Я экспериментирую с обработкой исключений в сопрограммах 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