What is the difference between GlobalScope and MainScope?
//Accessing data from Room
GlobalScope.launch {
v.tvStoreName.text = pfViewModel.getStoreName()
pageDetails.pageNumber = currentPage
pageDetails.pageSize = pageSize
pfViewModel.getTransactions(pageDetails, toolbarBuilder?.getDate()!!)
}
The GlobalScope sometimes makes an error which is very hard to reproduce.
Fatal Exception: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
MainScope().launch {
var storeName = ""
withContext(Dispatchers.Default) {
storeName = pfViewModel.getStoreName()
}
v.tvStoreName.text = storeName
}
CodePudding user response:
What is the difference between GlobalScope and MainScope?
MainScope
is a CoroutineScope
that uses the Dispatchers.Main
dispatcher by default, which is bound to the main UI thread.
The GlobalScope
is a CoroutineScope
that has no dispatcher in its coroutine context. This means that the coroutines launched in this scope will use the Dispatchers.Default
dispatcher, which is backed by a thread pool (sized based on the number of CPU cores you have).
The GlobalScope
also has no Job
in its context, which means structured concurrency doesn't apply. Coroutines launched in it are never cancelled automatically, so they need to be manually controlled. This is why it is usually discouraged to use it unless you have very specific needs.
Only the original thread that created a view hierarchy can touch its views.
This error occurs when you're trying to modify views from outside the main thread, which is what happens if you do this from coroutines launched in the GlobalScope
(because it's backed by a separate thread pool).
In your second snippet, you are using withContext(Dispatchers.Default)
, which only makes this part of the code run on that thread pool, but the rest runs on UI thread. This is why the update of the UI is ok there.
Note that Room already uses a dispatcher with a background thread pool for its queries, so you don't need to switch context manually like this, you can just call it from the UI thread.
Side note: using MainScope().launch { .. }
like this is a bad idea, because it suffers from the same cancellation issue as GlobalScope
. To use it properly, you would need to extract this scope into a variable/property so you can cancel it when appropriate. That said, it's easier to use an existing scope. Android already provides a ready-to-use coroutine scope in components such as Activities which have a lifecycle (see lifecycle-runtime-ktx
library). It's called lifecycleScope
. You should launch your coroutines in this scope so they automatically get cancelled when the activity is destroyed.