Я использую библиотеку OkHttp для загрузки некоторых данных из Интернета в свой androidx.lifecycle.ViewModel
Затем я хочу обновить свой LiveData
. Кажется, что выполнение этого из фонового потока вызывает исключение следующим образом:
2022-01-17 15:47:59.589 7354-7396/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: com.example.myapplication, PID: 7354
java.lang.IllegalStateException: Cannot invoke setValue on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:487)
at androidx.lifecycle.LiveData.setValue(LiveData.java:306)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at com.example.myapplication.MainActivityViewModel$getOneMoreCat$1.invoke(MainActivityViewModel.kt:86)
at com.example.myapplication.MainActivityViewModel$getOneMoreCat$1.invoke(MainActivityViewModel.kt:39)
at com.example.myapplication.singleton.CommunicationManager$sendRequest$1.onResponse(CommunicationManager.kt:24)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Теперь я нашел два разных способа отправки в основной поток из ViewModel
(который не имеет ссылки на контекст в соответствии с рекомендациями AAC), см. здесь:
GlobalScope.launch {
withContext(Dispatchers.Main) {
// do whatever, e.g. update LiveData
}
}
Или
Handler(Looper.getMainLooper()).post(Runnable {
// do whatever, e.g. update LiveData
})
Какой правильный путь? То есть наименее эффективен во время выполнения.
Обновление Я обнаружил, что также могу выполнять myLiveData.post()
, и это работает из фонового потока.
Тем не менее, я хотел бы знать, как правильно отправить работу в основной поток в современном Android под kotlin.
3 ответа
Внутренняя модель просмотра,
private val _downloading = MutableLiveData<Result<Boolean>>()
val downloading: LiveData<Result<Boolean>>
get() = _downloading
fun downloadFile() {
viewModelScope.launch {
try {
_downloading.value = Result.Loading
val result = withContext(Dispatchers.IO) {
// download something
}
_downloading.value = Result.Success(true)
} catch (ex: Exception) {
_downloading.value = Result.Failure(ex)
}
}
}
В действии/фрагменте
viewModel.downloading.observe(this, {
when (it) {
is Result.Failure -> TODO()
Result.Loading -> TODO()
is Result.Success -> TODO()
}
})
Result
— это закрытый класс для захвата состояния, что, в свою очередь, поможет нам соответствующим образом обновить пользовательский интерфейс. Также viewmodelscope
используется вместо GlobalScope
, так как мы не хотим, чтобы загрузка продолжалась, когда модель представления уничтожена.
Правильный способ перенаправить работу из фонового потока в основной поток с помощью LivaData
— использовать LivaData.postValue()
. Он отправляет задачу в основной поток, чтобы установить заданное значение.
Другой подход заключается в использовании свойства расширения viewModelScope
в ViewModel
, по умолчанию он использует контекст Dispatchers.Main
для выполнения сопрограммы, это означает, что вы можете обновить пользовательский интерфейс в такой сопрограмме. Например, в вашем классе ViewModel
:
viewModelScope.launch {
val result = makeNetworkCall()
// use result to update UI
liveData.value = result
}
// withContext - switches context to background thread
suspend fun makeNetworkCall(): String = withContext(Dispatchers.IO) {
delay(1000) // simulate network call
"SomeResult"
}
Зависимость для использования viewModelScope
:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
GlobalScope
крайне не рекомендуется использовать, его можно использовать только в определенных случаях, вот описание, почему его не использовать.
Есть много способов сделать это, вы можете просто отправить значение в живые данные, используя диспетчер и обработчик, который работает в основном потоке, поскольку вы предоставляете цикл основного потока.
Другой способ - вы можете использовать функции высокого порядка для обновления моделей просмотра, которые просты в использовании, и попробуйте.
Похожие вопросы
Связанные вопросы
Новые вопросы
android
Android - это мобильная операционная система Google, используемая для программирования или разработки цифровых устройств (смартфоны, планшеты, автомобили, телевизоры, одежда, стекло, IoT). Для тем, связанных с Android, используйте специальные теги Android, такие как android-intent, android-activity, android-адаптер и т. Д. Для вопросов, не связанных с разработкой или программированием, но связанных с платформой Android, используйте эту ссылку: https: // android.stackexchange.com .
LiveData
, вы можете просто использоватьpostValue
. Если есть какая-либо другая операция пользовательского интерфейса, вы можете использоватьDispatchers
. Кроме этого, не используйтеGlobalScope
. См. Это и Это.