Why does viewModelScope.launch run on the main thread by default
ViewModelScope.launch { }
runs on the main thread, but also gives you the option to run other dispatchers, so you can have UI & Background operations running synchronously.
For you example:
fun thisWillRunOnMainThread() { viewModelScope.launch { //below code will run on UI thread. showLoadingOnUI() //using withContext() you can run a block of code on different dispatcher val result = novel.id = withContext(Dispatchers.IO) { withsomeRepository.someWork() } //The below code waits until the above block is executed and the result is set. liveData.value = result finishLoadingOnUI() }}
For more reference, I would say there are some neat articles that will help you understand this concept.
So my second question is, is this the way to go ?
I would expect two things to be different in your current approach.
1.) First step would be to define the scheduler of the background operation via withContext
.
class SomeRepository { suspend fun doWork(): SomeResult = withContext(Dispatchers.IO) { ... }}
This way, the operation itself runs on a background thread, but you didn't force your original scope to be "off-thread".
2.) Jetpack Lifecycle KTX provides the liveData {
coroutine builder so that you don't have to postValue
to it manually.
val liveData: LiveData<SomeResult> = liveData { emit(someRepository.someWork())}
Which in a ViewModel, you would use like so:
val liveData: LiveData<SomeResult> = liveData(context = viewModelScope.coroutineContext) { withContext(Dispatchers.IO) { emit(someRepository.someWork()) }}
And now you can automatically trigger data-loading via observing, and not having to manually invoke viewModelScope.launch {}
.