Reputation: 979
I have the following code in which I launch a coroutine to handle the retrieving of data and storing them into the local database:
private var lastRequestedPage = 1
private var isRequestInProgress = false
private val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Main)
// function that is called from several places
private fun requestAndSaveData(){
if(isRequestInProgress) return
isRequestInProgress = true
viewModelScope.launch{
withContext(Dispatchers.IO){
// 2 heavyweight operations here:
// retrieve some data via Retrofit & place it into the local data via Room Persistence Library
}
}
lastRequestedPage++
isRequestInProgress = false
}
The network call and database operation is done based on a boolean value called isRequestInProgress
. When that is false, it is set to true, the network & database operations can be started by the coroutine and after that the lastRequestedPage
is incremented before we set isRequestInProgress
again to false so that the whole process can be started again by the program from any place.
Note that lastRequestedPage
is passed to the Retrofit network call function as argument since the web service from which the data comes uses Pagination (for the sake of brevity I left that out).
Can I assume that this logic works with a coroutine like this ? Can I have some bad threading issues with a solution like this ? I am asking because I am new to the concept of coroutines and I was adapting this logic from another project of mine where I used listeners&callbacks with Retrofit to perform asynchronous work(whenever the Retrofit#onResponse
method was called I incremented lastRequestedPage
variable and set the isRequestInProgress
to back to false).
Upvotes: 1
Views: 3353
Reputation: 18477
Short answer: No, this won't work. It's incorrect.
When you call viewModelScope.launch { }
or for that matter GlobalScope.launch { }
, the block inside the launch
gets suspended. And the program flow moves on to the next statement.
In your case, viewModelScope.launch { }
will suspend the call to withContext(...)
and move on to lastRequestedPage++
statement.
It will immediately increment the lastRequestedPage
and toggle isRequestInProgress
flag before even actually starting the request.
What you want to do is move those statements inside the launch { }
block.
This is how the flow works.
To get a better sense of how it works, try this code.
Log.d("cor", "Hello, world!") // Main thread
GlobalScope.launch {
// Suspend this block. Wait for Main thread to be available
Log.d("cor", "I am inside the launch block") // Main thread
withContext(Dispatchers.IO) {
delay(100L) // IO thread
Log.d("cor", "I am inside the io block") // IO thread
}
Log.d("cor", "IO work is done") // Back to main thread
}
Log.d("cor", "I am outside the launch block") // Main thread
The output is
D/cor: Hello, world!
D/cor: I am outside the launch block
D/cor: I am inside the launch block
D/cor: I am inside the io block
D/cor: IO work is done
Upvotes: 3