Difference Between CoroutineScope and coroutineScope in Kotlin

David Mbochi Njonge Feb 15, 2024
  1. Create a New Kotlin Project
  2. Create a Button on the Main Layout
  3. Use CoroutineScope in Kotlin
  4. Use coroutineScope in Kotlin
  5. Conclusion
Difference Between CoroutineScope and coroutineScope in Kotlin

The Kotlin documentation defines coroutine as an execution that can be suspended as it is waiting for some background tasks to be executed, like downloading a resource from a network.

Coroutine helps us achieve concurrency since other computations continue executing when a coroutine is suspended. Note that the coroutines are independent of the thread they are using because it is not guaranteed that they will resume execution on the thread they were using.

To create a new coroutine, we must do so inside a scope. In this tutorial, we will learn how the scope in unstructured concurrency affects child coroutines and how to solve the scope problem using structured concurrency.

Create a New Kotlin Project

This tutorial will utilize IntelliJ IDEA, but you can use any preferred development environment.

Open IntelliJ IDEA and select File > New > Project. On the window that opens, select Android on the bottom left side, then select Empty activity on the right side, as shown below.

Android Project - Empty Activity

Press the button labeled Next and on the window that opens, enter the project name as CoroutineScope, enter the package name as com.coffeedev.coroutinescope, select Kotlin on the Language section, and select API 19 on the Minimum SDK section.

Ensure these details are as shown below.

Project Details

Press the button labeled Create to generate a new Android project. This action creates a new application containing an activity named MainActivity and a layout called activity_main.

We will use these files to test the examples we cover in this tutorial. Ensure you have an active internet connection to add the required dependencies to our application.

To work with coroutines, we need to add the kotlinx-coroutines-core dependency to our project. Copy and paste the following dependency in the build.gradle file to add the coroutine dependency.

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
}

Create a Button on the Main Layout

Open the acivity_main.xml layout file under src/main/res/layout and copy and paste the following code into the file.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center_horizontal"
        tools:context=".MainActivity" >

    <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            android:text="@string/button_text"/>

</LinearLayout>

This code creates a LinearLayout that contains only one View of type Button. We will use this button to invoke the coroutines in our application.

Ensure the final layout is as shown below.

App Layout

The button is labeled using a string resource as indicated by the text attribute. Copy and paste the following string resource in the file named strings.xml located under the src/main/res/values folder.

This creates a text for our button, and this text is accessed using the name button_text.

<resources>
    <string name="app_name">CoroutineScope</string>
    <string name="button_text">Press Me</string>
</resources>

Use CoroutineScope in Kotlin

In the introduction section, we mentioned that to create a new coroutine, we must do so inside a scope. This is where the CoroutineScope comes in place.

To view this in action, copy and paste the code below into the MainActivity.kt file under the src/main/java/com/coffeedev/coroutinescope folder.

package com.coffeedev.coroutinescope

import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button = findViewById<Button>(R.id.button)

        button.setOnClickListener {
            CoroutineScope(Dispatchers.Main).launch {
                val message = getLoopProduct();
                Toast.makeText(applicationContext, "Message: $message", Toast.LENGTH_LONG)
                    .show();
            }
        }

    }

    private suspend fun getLoopProduct(): Int {
        var value = 1;
        CoroutineScope(Dispatchers.IO).launch {
            for (number in 1..5) {
                delay(15);
                value *= number;
            }
        }
        return value;
    }

}

In this code, we have created a suspend function named getLoopProduct() that returns the product of a for loop. The for loop is executed using a coroutine that runs using the Dispatchers.IO threads, passed as the argument of the CoroutineScope().

For each iteration of the for loop, there is a delay of 15 milliseconds, suspending the currently executing thread.

In the onCreate() life cycle method, we have created a new scope that runs using the Dispatchers.Main thread which is simply the main thread. Note that the coroutine of the getLoopProduct() is a child of the coroutine created inside the onCreate() method because the suspend function is invoked inside it.

Coroutines created from different scopes run independently. Since the child coroutine uses a different scope from the parent coroutine, the parent does not wait for the child to finish executing.

This type of execution is referred to as Unstructured Concurrency.

The coroutine in our onCreate() method executes only once and terminates. This means that the child coroutine continues running in the background and can expose our application to memory leaks.

We use the button created in our layout to display a Toast containing the value returned by the getLoopProduct(). The setOnClickListener() method displays the Toast on the screen when we press the button.

Run this code and note that the Toast displays a value of 1 since the parent coroutine was suspended before the child coroutine finished executing.

Output:

Unstructured Concurrency

Use coroutineScope in Kotlin

The difference between the CoroutineScope() and coroutineScope() is that the latter creates a new scope without creating a new coroutine. The child coroutine uses the parent coroutinescope, which ensures that it completes before the parent coroutine completes execution.

This type of execution is referred to as Structured Concurrency.

To view this in action, replace the suspend function in the previous example with the one provided below.

   private suspend fun getLoopProduct(): Int {
        var value = 1;
        coroutineScope {
            for (number in 1..5) {
                delay(15);
                value *= number;
            }
        }
        return value;
    }

Since the code in the onCreate() method does not change, the child coroutine uses the parent scope, which runs in the main thread to execute the for loop. The parent coroutine waits for the child coroutine to execute the for loop before it terminates.

Run this code and note that the Toast displays a value of 1 20. Two indicates that the child coroutine is executing the entire loop without termination due to reusing the parent scope.

Output:

Structured Concurrency

Conclusion

In this tutorial, we have learned how the scope of unstructured concurrency affects child coroutines and how to solve the problem using structured concurrency. The main topics we have covered are how to use CoroutineScope() to create independent scopes and how to use coroutineScope() to reuse the parent scope.

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub

Related Article - Kotlin Coroutine