Я думаю, что ViewModel (или BaseViewModel) должен быть уровнем, обрабатывающим исключения, потому что в этом слое лежит логика принятия решения UI, например, если мы просто хотим показать тост, игнорируем тип исключениявызов другой функции и т. д. *
Как вы думаете?
Конечно, вы правы.Сопрограмма должна срабатывать на ViewModel
, хотя логика в Repository
/ Service
.Вот почему Google уже создал специальный coroutineScope
с именем viewModelScope , в противном случае это будет "repositoryScope".Также сопрограммы имеют приятную функцию обработки исключений, которая называется CoroutineExceptionHandler
.Здесь все становится лучше, потому что вам не нужно реализовывать try{}catch{}
блок:
val coroutineExceptionHanlder = CoroutineExceptionHandler{_, throwable ->
throwable.printStackTrance()
toastLiveData.value = showToastValueWhatever()
}
Позже в ViewModel
coroutineScope.launch(Dispatchers.IO + coroutineExceptionHanlder){
val data = serviceOrRepo.getData()
}
Конечно, вы все еще можете использоватьблок try/catch
без CoroutineExceptionHandler
, свободный для выбора.
Обратите внимание, что в случае Retrofit вам не нужен планировщик Dispatchers.IO
, потому что Retrofit делает это за вас (начиная с Retrofit 2.6.0).
В любом случае, я не могу сказать, что статья плохая, но это не лучшее решение.Если вы хотите следовать руководству по статьям, вы можете проверить Преобразования в LiveData .
РЕДАКТИРОВАТЬ: Вам нужно больше знать, что сопрограммы небезопасны.Я имею в виду, что они могут вызывать утечки памяти, особенно в жизненном цикле Android в целом.Вам нужен способ отменить сопрограммы, пока Activity
/ Fragment
больше не живет.Так как ViewModel
имеет onCleared
(который вызывается, когда Activity
/ Fragment
уничтожен), это означает, что сопрограммы должны стрелять в одном из них.И, возможно, это главная причина, почему вы должны запустить сопрограмму в ViewModel
.Обратите внимание, что с viewModelScope
нет необходимости cancel
задание onCleared
.
Простой пример:
viewModelScope.launch(Dispatchers.IO){
val data = getDataSlowly()
withContext(Dispatchers.MAIN){
showData();
}
}
или без viewModelScope
:
val job = Job () val coroutineScope = CoroutineContext (Dispatchers.MAIN + job)
fun fetchData(){
coroutineScope.launch(){
val data = getDataSlowly()
withContext(Dispatchers.MAIN){
showData();
}
}
}
// позже в модели представления:
override fun onCleared(){
super.onCleared()
job.cancel() //to prevent leaks
}
В противном случае ваш Service
/ Repository
протечет.Другое примечание: NOT , чтобы использовать GlobalScope
в этом случае.