I'm just reading and learning about coroutines in Kotlin to use in a small library for fun/learning purposes. In the documentation, you can do something like
GlobalScope.launch {
}
So in my method,
fun myMethod() {
GlobalScope.launch {
// do some networking code
}
}
Is it best practice to use GlobalScope to launch a coroutine for a library? In the docs it says
Application code usually should use application-defined CoroutineScope, using async or launch on the instance of GlobalScope is highly discouraged.
This is obviously a library and not necessarily app code. But I wasn't sure if that was the best way to do networking in the background with coroutines.
I also tried
runBlocking {
async {
// do some networking code
}
}
Thinking that runBlocking introduces a new coroutine scope, but I think in this case it inherits the scope from its parents which is the main thread so then I get an exception about no networking on the main UI thread.
You can create a suspending function, and this will enforce that the user calls this from a coroutine themselves.
If your method uses withContext you don't have to worry about GlobalScope or configuring the scope. withContext simply tells the coroutine which context to use (for networking, you'll want IO. And the scope is determined now by however the user launches it.
The way I'd construct your method would be:
suspend fun myMethod() = withContext(Dispatchers.IO) {
// do some networking code
}
Depending on what you are trying to achieve, you can use different patterns even inside a library.
If your function is supposed to immediately return after launching some coroutines, the "best practice" is to declare your function as an extension of the CoroutineScope, that way you don't have to use the global scope:
fun CoroutineScope.launchesAndReturnsImmediately() {
// launch can be called because we are extending CoroutineScope
launch {
// some work
}
}
That being said, you often don't need to return immediately when in a library, so you can declare your functions suspend instead, which is easier to grasp on the consumer side. Multiple options there:
withContext to run on appropriate thread pool, or call other suspending functionscoroutineScope to launch child coroutines and decompose the work, but still suspend until all child coroutines are donesuspendCoroutine or suspendCancellableCoroutine