Just trying to play around with coroutines to mock...
# coroutines
c
Just trying to play around with coroutines to mock out what a long running DB operation or network call could look like if I wanted to Toast the result of it. I'm really just learning Kotlin and coroutines so they are crude examples. Can anyone tell me what's wrong about my coroutine here? I'm finding it coroutines tough to understand.
Copy code
fun button1Clicked() {
GlobalScope.launch {
Toast.makeText(this@MainActivity, doSomethingLong(), Toast.LENGTH_LONG).show()
}
Toast.makeText(this@MainActivity, "Hello", Toast.LENGTH_LONG).show()
}

suspend fun doSomethingLong(): String =
withContext(Dispatchers.Default) {
delay(3000)
return@withContext "World"
}
e
What’s it not doing? Also, should you be running the UI modification code in
Dispatchers.Main
? That will cause those toast calls to run on the UI thread rather than in GlobalScope.
c
Sorry. Should've explicitly stated that it's crashing on the Toast inside of the GlobalScope.launch {}. @Evan R. I would have expected GlobalScope.launch{} to run on the dispatcher that it was called on? Why would it run on a different dispatcher if I didn't tell it to? Excuse my ignorance if I'm missing something here...
a
try this:
Copy code
fun button1Clicked() {
GlobalScope.launch(Main) {
val result = withContext(IO) {  doSomethingLong() }
Toast.makeText(this@MainActivity, result, LENGTH_LONG).show()
}
e
@Colton Idle from https://kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html :
The default dispatcher that is used when coroutines are launched in GlobalScope is represented by Dispatchers.Default and uses a shared background pool of threads, so launch(Dispatchers.Default) { ... } uses the same dispatcher as GlobalScope.launch { ... }.
.launch()
only inherits a scope when it is launched within an existing coroutine scope. Because you were using GlobalScope as its scope and did not specify a different one in the parameter to
.launch()
, it used Dispatchers.Default instead of Dispatchers.Main.
c
Thanks for the explanation @Evan R. and for the snippet of code @ahulyk Last question here. Are one of these better/more idiomatic than the other?
Copy code
fun button1Clicked() {
        GlobalScope.launch(Dispatchers.Main) {
            val result = withContext(<http://Dispatchers.IO|Dispatchers.IO>) { doSomethingLong() }
            Toast.makeText(this@MainActivity, result, Toast.LENGTH_SHORT).show()
        }
    }

    suspend fun doSomethingLong(): String =
        withContext(Dispatchers.Default) {
            delay(3000)
            return@withContext "World"
        }
vs
Copy code
fun button1Clicked() {
        GlobalScope.launch(Dispatchers.Main) {
            val result = withContext(<http://Dispatchers.IO|Dispatchers.IO>) { doSomethingLong() }
            Toast.makeText(this@MainActivity, result, Toast.LENGTH_SHORT).show()
        }
    }

    suspend fun doSomethingLong(): String {
        delay(3000)
        return "World"
    }
I know this is a small/dumb example, but maybe there's a best way to do this? Should I always try to have the scope declared inside of the method doing the long running work as best practice?
e
It would be more idiomatic to have your Activity implement the CoroutineScope interface. You can see an example in the documentation: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/ The reason this is good is because when the activity is stopped, you can immediately cancel all your coroutines and not worry about resource cleanup. In addition, it should make it so you don’t even need GlobalScope!
c
I think I saw some of this scope stuff related to ViewModels in Android as well. I will look into that. Thank you both again!
e
Good luck!
👍 1
g
No need to implement CoroutineScope explicitly in Activity, instead use coroutineScope extension of Lifecycle or LifecycleOwner
☝️ 3