Scopes in Kotlin Coroutines

Prerequisites:

Scope in Kotlin’s coroutines can be defined as the restrictions within which the Kotlin coroutines are being executed. Scopes help to predict the lifecycle of the coroutines. There are basically 3 scopes in Kotlin coroutines:

  1. Global Scope
  2. LifeCycle Scope
  3. ViewModel Scope

Dependencies to be Imported in Build.gradle (app level file)

Import following dependencies to build.gradle (app) level file.

implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5’

implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5’



def arch_version = ‘2.2.0-alpha01’

implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:$arch_version”

implementation “androidx.lifecycle:lifecycle-runtime-ktx:$arch_version”

1. Global Scope

Global Scope is one of the ways by which coroutines are launched. When Coroutines are launched within the global scope, they live long as the application does. If the coroutines finish it’s a job, it will be destroyed and will not keep alive until the application dies, but let’s imagine a situation when the coroutines has some work or instruction left to do, and suddenly we end the application, then the coroutines will also die, as the maximum lifetime of the coroutine is equal to the lifetime of the application. Coroutines launched in the global scope will be launched in a separate thread. Below is the example which shows that the in global scope coroutines are launched in a separate thread.

Kotlin

filter_none

edit
close

play_arrow

link
brightness_4
code

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
  
class MainActivity : AppCompatActivity() {
    val TAG = "Main Activity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        GlobalScope.launch {
            Log.d(TAG, Thread.currentThread().name.toString())
        }
        Log.d("Outside Global Scope", Thread.currentThread().name.toString())
    }
}

chevron_right


Below is the Log-Output for the above program:

Log Output

As it is known that coroutines launched in global scope live as long as the application does, but there are very rare chances when the developer needs the coroutines to be live as long as the application does. The main problem with the coroutines launched in the global scope is that when the activity in which coroutines is launched dies, the coroutines will not die with the activity, since the lifetime of coroutines is decided on the basis of application lifetime, not the activity lifetime. Since the coroutine is using the resources of the activity in which it is launched, and now since that activity has died, the resources will not be garbage collected as a coroutine is still referring to that resources. This problem can lead to a memory leak. So using global scope all the time is not always a good idea. Let’s try to launch a coroutine and run an infinite loop with a delay of 1 sec and launch another coroutine within the global scope after the delay of 5sec from the starting by terminating the first activity and intent to another activity. we can see in the output that even after the first activity is being terminated programmatically, the coroutine associated with the first activity does not die. Let’s try to understand what written in the above paragraph programmatically. Below is the code for both the activity_main.xml and the MainActivity.kt file.



XML

filter_none

edit
close

play_arrow

link
brightness_4
code

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
  
    <Button
        android:id="@+id/btnStartActivity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="167dp"
        android:layout_marginTop="320dp"
        android:layout_marginEnd="156dp"
        android:layout_marginBottom="363dp"
        android:text="Start Activity"
        android:textSize="22sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

chevron_right


Kotlin

filter_none

edit
close

play_arrow

link
brightness_4
code

// program for main activity which intent to another activity
// it uses global scope to launch the coroutine
  
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
  
const val TAG = "Main Activity"
  
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        btnStartActivity.setOnClickListener {
            // coroutine will launch when button got pressed
            GlobalScope.launch {
                // infinite loop
                while (true) {
                    delay(1000L)
                    Log.d(TAG, "Still Running..")
                }
            }
  
            GlobalScope.launch {
                delay(5000L)
                // new activity will get intended after 5 sec
                val intent = Intent(this@MainActivity, SecondActivity::class.java)
                startActivity(intent)
                // calling finish() method,so that first activity will not be alive
                // after being intended to second activity
                finish()
            }
        }
    }
}

chevron_right


Go to app > java > 1st package name > right-click > New > Activity > Empty Activity to cretae a new activity and named it as SecondActivity. Below is the code for both the activity_second.xml and SecondActivity.kt file.

XML

filter_none

edit
close

play_arrow

link
brightness_4
code

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SecondActivity">
  
    <!--Various attributes for button-->
    <Button
        android:id="@+id/btnSecondActivity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="166dp"
        android:layout_marginTop="331dp"
        android:layout_marginEnd="130dp"
        android:layout_marginBottom="352dp"
        android:gravity="center"
        android:text="Second Activity"
        android:textSize="22sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

chevron_right


Kotlin

filter_none

edit
close

play_arrow

link
brightness_4
code

// program for second activity
  
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
  
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        Log.i("Second Activity", "Second Activity Running ....")
        Toast.makeText(this, "Second Activity", Toast.LENGTH_SHORT).show()
    }
}

chevron_right


Output: 

It can be seen in the log out below that even after the second activity is being launched, the coroutine of the main activity is still running. The oval circle is used to show the timestamps.

Log Output

2. LifeCycle Scope

The lifecycle scope is the same as the global scope, but the only difference is that when we use the lifecycle scope, all the coroutines launched within the activity also dies when the activity dies. It is beneficial as our coroutines will not keep running even after our activity dies. In order to implement the lifecycle scope within our project just launch the coroutine in lifecycle scope instead of global scope, ie just change the global scope to lifecycle scope in the main activity within which the infinite loop is running. All the code will remain the same except for some changes in the code of the main activity as mentioned above.

Kotlin

filter_none

edit
close

play_arrow

link
brightness_4
code

// program to show how lifecycle scope works
  
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
  
const val TAG = "Main Activity"
  
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        btnStartActivity.setOnClickListener {
            // launching the coroutine in the lifecycle scope
            lifecycleScope.launch {
                while (true) {
                    delay(1000L)
                    Log.d(TAG, "Still Running..")
                }
            }
  
            GlobalScope.launch {
                delay(5000L)
                val intent = Intent(this@MainActivity, SecondActivity::class.java)
                startActivity(intent)
                finish()
            }
        }
    }
}

chevron_right


Log Output: 

The oval circle is used to show the timestamps.

Log Output

It can be seen in the above log output that the main activity stops get printing after the launch of the second activity.

3. ViewModel Scope

It is also the same as the lifecycle scope, only difference is that the coroutine in this scope will live as long the view model is alive. ViewModel is a class that manages and stores the UI-related data by following the principles of the lifecycle system in android. If one wants to dig deeper into what basically view model class is can refer to this link of android official docs. 




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :
Practice Tags :


Be the First to upvote.


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.