Почему уход с экрана логирует сетевую ошибку и оставляет временный файл?
Загрузка идёт в viewModelScope. Каждый раз, когда пользователь уходит с экрана посреди загрузки, приложение логирует «download failed: network error», а недокачанный временный файл не удаляется.
Ограничения: уход с экрана должен быть тихим и обязан останавливать загрузку, недокачанный файл должен удаляться всегда, а настоящий сетевой сбой — по-прежнему логироваться. deleteTemp() — suspend.
fun download(url: String) = viewModelScope.launch(Dispatchers.IO) {
try {
httpClient.writeTo(url, tempFile) // suspending
} catch (e: Exception) {
Log.e("dl", "download failed: network error", e)
} finally {
deleteTemp(tempFile) // suspending
}
}
Определите причину и исправьте её.
Отмена приходит как CancellationException, а catch (e: Exception) её проглатывает — обычная отмена логируется как сетевой сбой. Приостанавливающий deleteTemp() в finally затем падает сразу, ведь Job уже отменён. Перебросьте отмену; очистку — в NonCancellable.
- ✗Ловить
Exception(или братьrunCatching) вокруг приостанавливающего вызова и глотать отмену - ✗Сообщать о
CancellationExceptionпользователю как о настоящем сбое - ✗Ждать, что приостанавливающая очистка в
finallyотработает в отменённой корутине
- →Почему
runCatchingздесь опаснее, чемcatch (e: Exception)? - →Какие сбои этот
catchзагрузки всё же должен обрабатывать сам?
Причина
Две ошибки, обе связаны с тем, что отмена — это исключение.
Отмена перестаёт распространяться, а в лог уходит несуществующий «сетевой сбой». runCatching опаснее вдвое: он ловит Throwable.
сразу же — включая deleteTemp() в finally. Файл остаётся на диске.
CancellationException— обычный потомокException, поэтомуcatch (e: Exception)ловит и его.- В отменённой корутине
Jobуже не активен, поэтому любой приостанавливающий вызов падает
Исправление
fun download(url: String) = viewModelScope.launch(Dispatchers.IO) {
try {
httpClient.writeTo(url, tempFile)
} catch (e: CancellationException) {
throw e // отмену пропускаем дальше
} catch (e: IOException) {
Log.e("dl", "download failed: network error", e)
} finally {
withContext(NonCancellable) { // очистка переживает отмену
deleteTemp(tempFile)
}
}
}
CancellationException перебрасывается — уход с экрана снова тихий, а корутина действительно останавливается. Настоящий сетевой сбой ловит узкий catch (e: IOException). Очистка в NonCancellable доводится до конца; держите этот участок коротким — остановить его уже нечем.