Меня особенно беспокоит вставка пользовательских данных в локальную базу данных.
В примерах преобладает следующая схема (в том числе из официальных источников, например JetBrains, Google / Android)для использования сопрограмм Kotlin в сочетании с [компонентами архитектуры Android] ViewModels.
class CoroutineScopedViewModel : ViewModel(), CoroutineScope {
private val _job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + _job
override fun onCleared() {
super.onCleared()
_job.cancel()
}
fun thisIsCalledFromTheUI() = launch {
/* do some UI stuff on the main thread */
withContext(Dispatchers.IO) {
try {
/* do some IO, e.g. inserting into DB */
} catch (error: IOException) {
/* do some exception handling */
}
}
}
}
В моем понимании документации я понимаю, что в приведенном выше примере сопрограммы запускались в контексте пользовательского интерфейса (определяется черезcoroutineContext
) будет отменен, когда ViewModel
будет уничтожен, но код в блоке withContext(Dispatchers.IO)
будет запущен до конца.
Но прежде чем я начну рефакторинг своего проекта из ((pre-1.0.0) модель сопрограммы в глобальном масштабе (launch / async), я чувствую, что мне нужно просто уточнить некоторые вещи:
Правильно ли я прочитал документацию?Или уничтожение модели представления перед выполнением блока withContext(Dispatchers.IO)
до завершения также приведет к отмене этого задания?Т.е. может ли эта модель использоваться для вставки данных в мою БД, или может возникнуть какая-то странная проблема с синхронизацией, когда пользователь наносит ответный удар или иным образом заставляет владельца ViewModel закрываться, что приводит к потере данных?
Не знаюхочу случайно ввести ошибку синхронизации, потому что я что-то неправильно понял и поэтому преобразовал свой код в модель, аналогичную показанной выше.
РЕДАКТИРОВАТЬ:
Итак, я решил провести небольшой тест,и мне кажется, что все эти примеры, использующие эту модель для записи в базу данных, могут иметь фундаментальную ошибку.
Изменение кода для регистрации того, что происходит, например:
class ChildViewModel : ViewModel(), CoroutineScope {
private val _job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + _job
override fun onCleared() {
super.onCleared()
Log.d("onCleared", "Start")
_job.cancel()
Log.d("onCleared", "End")
}
fun thisIsCalledFromTheUI() = launch {
Log.d("thisIsCalledFromTheUI", "Start")
GlobalScope.launch(Dispatchers.IO) {
Log.d("GlobalScope", "Start")
delay(15000)
Log.d("GlobalScope", "End")
}
withContext(Dispatchers.IO) {
Log.d("withContext", "Start")
delay(10000)
Log.d("withContext", "End")
}
Log.d("thisIsCalledFromTheUI", "End")
}
}
Результатыв этом случае, если вы дадите ему завершиться:
D/thisIsCalledFromTheUI: Start
D/GlobalScope: Start
D/withContext: Start
D/withContext: End
D/thisIsCalledFromTheUI: End
D/GlobalScope: End
Но если вы закроете Fragment / Activity (не приложение) до окончания withContext
, вы получите следующее:
D/thisIsCalledFromTheUI: Start
D/GlobalScope: Start
D/withContext: Start
D/GlobalScope: End
Что указывает, по крайней мере мне, на то, что вы не можете использовать это для записи нестационарных данных в БД.