ViewModel
Architecture component calls the repository layer on the main thread totrigger the network request. This guide iterates through various solutionsthat use coroutines keep the main thread unblocked.ViewModel
includes a set of KTX extensions that work directly withcoroutines. These extension arelifecycle-viewmodel-ktx
library and are usedin this guide.build.gradle
file:onDraw()
, which causes your app to freeze and potentiallyleads to an Application Not Responding (ANR) dialog. For a better userexperience, let's run this operation on a background thread.Repository
class and see how it'smaking the network request:makeLoginRequest
is synchronous and blocks the calling thread. To modelthe response of the network request, we have our own Result
class.ViewModel
triggers the network request when the user clicks, forexample, on a button:LoginViewModel
is blocking the UI thread whenmaking the network request. The simplest solution to move the executionoff the main thread is to create a new coroutine and execute the networkrequest on an I/O thread:login
function:viewModelScope
is a predefined CoroutineScope
that is included withthe ViewModel
KTX extensions. Note that all coroutines must run in ascope. A CoroutineScope
manages one or more related coroutines.launch
is a function that creates a coroutine and dispatches theexecution of its function body to the corresponding dispatcher.Dispatchers.IO
indicates that this coroutine should be executed on athread reserved for I/O operations.login
function is executed as follows:login
function from the View
layer on the main thread.launch
creates a new coroutine, and the network request is madeindependently on a thread reserved for I/O operations.login
function continues executionand returns, possibly before the network request is finished. Note thatfor simplicity, the network response is ignored for now.viewModelScope
, it is executed inthe scope of the ViewModel
. If the ViewModel
is destroyed because theuser is navigating away from the screen, viewModelScope
Bluestacks china 4. is automaticallycancelled, and all running coroutines are canceled as well.makeLoginRequest
needs to remember to explicitly move the execution offthe main thread. Let's see how we can modify the Repository
to solvethis problem for us.makeLoginRequest
function is not main-safe, as callingmakeLoginRequest
from the main thread does block the UI. Use thewithContext()
function from the coroutines library to move the executionof a coroutine to a different thread:withContext(Dispatchers.IO)
moves the execution of the coroutine to anI/O thread, making our calling function main-safe and enabling the UI toupdate as needed.makeLoginRequest
is also marked with the suspend
keyword. This keywordis Kotlin's way to enforce a function to be called from within a coroutine.Dispatchers
into aRepository
layer. To learn more, seeTesting coroutines on Android.LoginViewModel
.As makeLoginRequest
moves the execution off the main thread, the coroutinein the login
function can be now executed in the main thread:makeLoginRequest
isa suspend
function, and all suspend
functions must be executed ina coroutine.login
example in a couple of ways:launch
doesn't take a Dispatchers.IO
parameter. When you don'tpass a Dispatcher
to launch
, any coroutines launched fromviewModelScope
run in the main thread.login()
function from the View
layer on the main thread.launch
creates a new coroutine to make the network request on the mainthread, and the coroutine begins execution.loginRepository.makeLoginRequest()
now suspends further execution of the coroutine until the withContext
block in makeLoginRequest()
finishes running.withContext
block finishes, the coroutine in login()
resumesexecution on the main thread with the result of the network request.View
from the ViewModel
layer, useLiveData
as recommended in theGuide to app architecture. When following this pattern,the code in the ViewModel
is executed on the main thread, so you can callMutableLiveData
's setValue()
function directly.Repository
layer can throw, use Kotlin'sbuilt-in support for exceptions.In the following example, we use a try-catch
block:makeLoginRequest()
call is handled as an error in the UI.