Issue
I am using OkHttp library to download some data from the internet in my androidx.lifecycle.ViewModel
I then want to update my LiveData
. It seems that doing it from background thread throws exception like so:
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)
Now I found two different ways to dispatch to main thread from ViewModel
(which has no reference to Context as per AAC guidelines), see here:
GlobalScope.launch {
withContext(Dispatchers.Main) {
// do whatever, e.g. update LiveData
}
}
or
Handler(Looper.getMainLooper()).post(Runnable {
// do whatever, e.g. update LiveData
})
Which is the correct way? That is, least impactful at runtime.
Update I did find that I can also do myLiveData.post()
and it works from background thread.
Still, I'd like to know what is the correct way to dispatch work to main thread in modern Android under kotlin
Solution
The right way to dispatch work from Background Thread to Main Thread using LivaData
is to use LivaData.postValue()
method. It posts a task to a main thread to set the given value.
Another approach is to use viewModelScope
extension property in ViewModel
class, by default it uses Dispatchers.Main
context to execute a coroutine, it means you can update UI in such coroutine. For example, in your ViewModel
class:
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"
}
Dependency to use viewModelScope
:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
GlobalScope
is highly discouraged to use, it can only be used in specific cases, here is a description why not use it.
Answered By - Sergey
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.