DEV Community

Cover image for Why the Global Scope isn't advised to be used?
João Esperancinha
João Esperancinha

Posted on • Edited on

Why the Global Scope isn't advised to be used?

The GlobalScope is a kind of scope in the world of Kotlin coroutines that challenges a lot of developers and prompts many to say that it is not useful and it was probably a bad idea to introduce it in the kotlix standard library any ways since the use of it is highly not recommended. The reason for this seems to stem of of the fact that we cannot tie the GlobalScope created coroutines with any specific lifecycle. This can be problematic in any kind of programming, but especially android programming where we mostly tie coroutines to the lifecycle of fragments or activities. In the video, to which you can find the link bellow, I am casually talking about that while walking in Gouda by the Groenhovenpark.

To exemplify how the GlobalScope operates and why it can be a problem to use it I created an example that you can by issuing these commands for example:

git clone https://github.com/jesperancinha/jeorg-kotlin-test-drives.git
cd jeorg-kotlin-coroutines/coroutines-crums-group-1
make b
Enter fullscreen mode Exit fullscreen mode

The example class is GlobalScopeCoroutine which is an executable clase and the code is this one:

class GlobalScopeCoroutine {
    companion object {
        @OptIn(DelicateCoroutinesApi::class)
        @JvmStatic
        fun main(args: Array<String> = emptyArray()) {
            val job = GlobalScope.launch {
                delay(1.seconds.toJavaDuration())
            }
            println("Global >> Is the Global job cancelled? ${job.isCancelled}")
            println("Global >> Is the Global job active? ${job.isActive}")
            println("Global >> Is the Global job completed? ${job.isCompleted}")
            job.cancel()
            println("Global >> Is the Global job cancelled after cancel? ${job.isCancelled}")
            runBlocking {
                val jobInScope = launch {
                    delay(1.seconds.toJavaDuration())

                }
                jobInScope.cancel()
                println("First Job >> Is this job cancelled? ${jobInScope.isCancelled}")
            }
            val lastJob = runBlocking {
                val jobInScope = launch {
                    delay(1.seconds.toJavaDuration())

                }
                println("Second Job >> Is this job cancelled? ${jobInScope.isCancelled}")
                println("Second Job >> Is this job active? ${jobInScope.isActive}")
                println("Second Job >> Is this job completed? ${jobInScope.isCompleted}")
                jobInScope
            }
            println("Second Job After Life-Cycle >> Is this job cancelled? ${lastJob.isCancelled}")
            println("Second Job After Life-Cycle >> Is this job active? ${lastJob.isActive}")
            println("Second Job After Life-Cycle >> Is this job completed? ${lastJob.isCompleted}")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

In first part of the code we can launch a coroutine wih the GlobalScope. Immediately we should observe that we are able to launch this coroutine without the need to already be in a Global scope. In this case, we only have access to our job in our localstack. This is the only place where we have access to a provision to be able to cancel that coroutine. We can also observe that nothing else is bound to that coroutine and that is is now running without any attachment to any lifecycle. After this call in a real program this coroutine would be running in the background until it would stop or it would remain running in case of a problem without us being able to stop it anywhere in the application. We can of course still cancel it in this localstack.

In the following runBlocking coroutine builder, we launch a coroutine in scope and we can observe that we can also cancel it in the same way as we did before with the GlobalScope.

It is in the last runBlocking call that we can observe something different. In this case, we do not cancel the coroutine and instead we just let it run until the the lifecycle ends. We can finally observe that this coroutine is not completed in the blocking scope but when it comes out, it does gets completed:

Global >> Is the Global job cancelled? false
Global >> Is the Global job active? true
Global >> Is the Global job completed? false
Global >> Is the Global job cancelled after cancel? true
First Job >> Is this job cancelled? true
Second Job >> Is this job cancelled? false
Second Job >> Is this job active? true
Second Job >> Is this job completed? false
Second Job After Life-Cycle >> Is this job cancelled? false
Second Job After Life-Cycle >> Is this job active? false
Second Job After Life-Cycle >> Is this job completed? true
Enter fullscreen mode Exit fullscreen mode

It is always quite difficult to explain this phenomenon, but the whole idea is that, if we have our framework manage the lifecycle of the coroutines we use then that is much better than using a detached scope like the GlobalScope.

In the literature we find that GlobalScope may still be used of some exceptional purposes like logging and monitoring. However for that purpose there are already several frameworks that allow us to do so and this is the reason why for the most part, GlobalScope is always 100% not advised to use.

You can find the video right over here:

Top comments (0)