Есть несколько вещей, которые играют важную роль в вашем случае использования
Здесь все хорошо, сбой Coroutine # 1 не влияет ни на родительский элемент, ни на Coroutine # 2. Это цель наблюдения
- CoroutineExceptionHandler против Thread.uncaughtExceptionHandler
CoroutineExceptionHandler - это обработчик по умолчанию, который будет распечатайте детали исключения после того, как исключение выдается сопрограммой. Использование launch
с join заставит сопрограмму ждать, пока задания не будут завершены, поэтому вы можете видеть выходные данные обеих сопрограмм.
Теперь, если сопрограмма потерпела крах с присоединиться , тогда он выдаст CancellationException
В частности, это означает, что a parent coroutine invoking join on a
child coroutine that was started using launch(coroutineContext) { ...
} builder throws CancellationException if the child had crashed
, если в контексте не установлен нестандартный CoroutineExceptionHandler .
CoroutineExceptionHandler без объединения : по умолчанию CoroutineExceptionHandler будет игнорировать CancellationException
, а если вы не используете join
, то оно выиграет ' не печатать ничего.
CoroutineExceptionHandler с объединением : если вы используете объединение в сопрограмме, то строитель выдаст CancellationException
, и поскольку задание еще не завершено (другое сопрограммы все еще выполняются), затем он напечатает ошибку и продолжит выполнение других заданий.
supervisorScope.coroutineContext[Job]!!.children.forEach { it.join() }
Следуйте тому же поведению, определенному с помощью Распространение исключений , где GlobalScope не имеет связанного Job
объекта.
В Android, Thread.uncaughtExceptionHandler - это обработчик по умолчанию, который убьет приложение в случае необработанного исключения и покажет диалоговое окно cra sh.
Разница между обработкой исключений с * или без join
в разных экосистемах, следовательно, вы не получаете поведения завершения в своем тесте kotlin с join
(которого нет в приложении android)
Хотя lifecycleScope.coroutineContext имеет значение SupervisorJob () + Dispatchers Главное, здесь я вижу, что отказ от сопрограммы ребенка затронул родителей и других детей.
Нет, ребенок не влияет на родительскую сопрограмму , потому что ребенка вообще нет. Обе ваши сопрограммы будут выполняться в том же потоке, что и отдельные родительские сопрограммы, и в ваших сопрограммах отсутствует отношение «родитель-потомок» (используйте Thread.currentThread () ?. name для просмотра имени потока), поэтому в случае исключения родительский объект делегирует исключение из
uncaughtExceptionHandler
из android, которое убивает приложение (см. пункт 1).
Итак, вы можете использовать withContext
lifecycleScope.launch(Dispatchers.Default) {
for (i in 0 until 5) {
Log.d(TAG, "testSupervisorScope: Coroutine #1: $i")
delay(100)
}
try {
// can use another context to change thread, e.g Dispatchers.IO
withContext(lifecycleScope.coroutineContext) {
Log.d(TAG, "testSupervisorScope: Coroutine withContext start")
delay(100)
throw RuntimeException("Coroutine sub-task failure")
}
} catch (e: java.lang.RuntimeException) {
e.printStackTrace()
}
}
или для того, чтобы установленные sh отношения родитель-потомок использовали ту же область действия для вызова дочерних сопрограмм, что и
private fun testSupervisorScope() = runBlocking {
// Coroutine #1
lifecycleScope.launch(Dispatchers.Default) {
for (i in 0 until 5) {
Log.d(TAG, "testSupervisorScope: Coroutine #1: $i")
delay(100)
}
// Coroutine child #1
try {
childCoroutineWithException().await()
} catch (e: Exception) {
Log.d(TAG, "caught exception")
e.printStackTrace()
}
}
}
// Note: use same scope `lifecycleScope` to ceate child coroutine to establish parent-child relation
fun childCoroutineWithException(): Deferred<String> = lifecycleScope.async {
Log.d(TAG, "testSupervisorScope: Coroutine child #1 start")
delay(100)
throw RuntimeException("Coroutine child #1 failure")
}
После того, как отношения родитель-потомок установлены, приведенный выше код сможет обрабатывать исключение в блоке catch
и не повлияет на выполнение других дочерних сопрограмм .
Результат с дочерними сопрограммами:
CoroutineJobActivity: testSupervisorScope: Coroutine #1: 1
CoroutineJobActivity: testSupervisorScope: Coroutine #1: 2
CoroutineJobActivity: testSupervisorScope: Coroutine #1: 3
CoroutineJobActivity: testSupervisorScope: Coroutine #1: 4
CoroutineJobActivity: testSupervisorScope: Coroutine #1: 5
CoroutineJobActivity: testSupervisorScope: Coroutine child #1 start
CoroutineJobActivity: Coroutine child #1 failure
Вы можете еще больше упростить свой пример, удалив runBlocking
private fun testSupervisorScope(){
// Coroutine #1
lifecycleScope.launch(Dispatchers.Default) {
for (i in 0 until 5) {
Log.d(TAG, "testSupervisorScope: Coroutine #1: $i")
try {
childCoroutineWithException().await()
} catch (e: Exception) {
Log.d(TAG, "caught exception")
e.printStackTrace()
}
delay(100)
}
}
}
// Note: use same scope `lifecycleScope` to ceate child coroutine to establish parent-child relation
fun childCoroutineWithException(): Deferred<String> = lifecycleScope.async {
Log.d(TAG, "testSupervisorScope: Coroutine child #1 start")
delay(100)
throw RuntimeException("Coroutine child #1 failure")
}
Вы можете реализовать свой собственный обработчик для необработанных исключений, чтобы избежать app cra sh с помощью (Не делайте этого, если вам это действительно не нужно, потому что это b рекламная практика, причины Техническая задолженность ).
Необходимо обработать необработанное исключение и отправить файл журнала