В соответствии с документацией GlobalScope
я думаю, что мы можем полагаться на то, что сопрограмма, запущенная с использованием глобального CoroutineScope, всегда выполняется.Документация гласит:
Глобальная область используется для запуска сопрограмм верхнего уровня, которые работают в течение всего времени жизни приложения и не отменяются преждевременно.
Я реализовалнекоторый тестовый код, и когда job
был отменен внутри UserViewModel
, сопрограмма в хранилище продолжала выполняться.Вот код с моими комментариями:
class UserViewModel(): CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
fun showUser() {
launch {
val repo = Repository()
val userDeferred = repo.getUser()
// if onClean() is called before the coroutine in Repository finishes,
// this line will not be called, but coroutine in Repository will continue executing
val result = userDeferred.await() // wait for result of I/O operation without blocking the main thread
}
}
fun onClean() {
job.cancel()
}
}
class Repository {
fun getUser() = GlobalScope.async {
delay(4000)
// this line is executed no matter whether the job in UserViewModel was canceled or not
"User returned"
}
}
Дополнительно мы можем уменьшить showUser()
функцию:
fun showUser() = repo.getUser().then(this) {
// `it` contains the result
// here is the main thread, use `it` to update UI
}
, используя функцию расширения then
:
fun <T> Deferred<T>.then(scope: CoroutineScope = GlobalScope, uiFun: (T) -> Unit) {
scope.launch { uiFun(this@then.await()) }
}
Если вы разрабатываете для Android и хотите быть уверенными, что ваша операция ввода-вывода выполнена полностью даже после очистки ViewModel, используйте WorkManager .Он предназначен для асинхронных и отложенных задач, требующих гарантии того, что система будет выполнять их, даже если приложение завершит работу.