Skip to content

CoroutineScope, CoroutineContext — Discussed in depth to understand better- Part 1

Published: at 6 min read

Table of content

CoroutineContext - Explain

A coroutine context is represented by the CoroutineContext interface. The CoroutineScope interface inherits from CoroutineContext and is commonly used to create and manage coroutines.

Most important CoroutineContext elements are :-

  1. Dispatcher(CoroutineDispatcher) - Specifies the thread or threads on which the coroutine runs. Common dispatchers include Dispatchers.Default for CPU-intensive work, Dispatchers.IO for I/O-bound work, and Dispatchers.Main for updating the UI.

  2. Job - Represents the lifecycle of the coroutine and allows to control the execution of the coroutine, cancel it, and check its status.

  3. Error handler (CoroutineExceptionHandler) - Handles uncaught exceptions that occur in the coroutine. It allows you to define a custom handler for exceptions.

  4. CoroutineName - Provides a name for the coroutine, which can be useful for debugging and logging purposes.

  5. CoroutineScope - Defines the scope of a coroutine and is a special case of CoroutineContext. It provides a convenient way to launch coroutines.

CoroutineScope

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.*

class MainViewModel : ViewModel() {

    // CoroutineScope associated with the ViewModel
    private val viewModelScope = CoroutineScope(Dispatchers.Main + SupervisorJob())

    fun performBackgroundTask() {
        viewModelScope.launch {
            // Coroutine code
            val result = fetchDataFromNetwork()
            updateUi(result)
        }
    }

    private suspend fun fetchDataFromNetwork(): String {
        // Simulate a network request
        delay(1000)
        return "Data from network"
    }

    private fun updateUi(result: String) {
        // Update the UI on the main thread
        println("Updating UI with result: $result")
    }

    override fun onCleared() {
        super.onCleared()
        // Cancel all coroutines when the ViewModel is cleared (e.g., when the associated UI component is destroyed)
        viewModelScope.cancel()
    }
}

In the above code snippet:

Coroutine Dispatcher

Several built-in dispatchers are present in kotlin Coroutine, each serving a specific purpose:

  1. Default (Dispatchers.Default):

    • It is optimized for CPU-intensive work.
    • It is backed by a shared pool of threads.
  2. IO (Dispatchers.IO):

    • It is optimized for I/O-intensive work, such as network or disk operations.
    • It uses a larger pool of threads compared to the default dispatcher.
  3. Main (Dispatchers.Main):

    • It is designed for UI-related work in Android applications.
    • It is typically used for updating the user interface.
    • It is available through the kotlinx-coroutines-android library in Android.
  4. Unconfined (Dispatchers.Unconfined):

    • It executes the coroutine in the current thread until it is suspended and resumes it in the same thread.
    • It is not suitable for CPU-intensive or long-running operations.
  5. Custom Dispatchers:

    • One can create custom dispatchers using newSingleThreadContext or newFixedThreadPoolContext functions, allowing to specify the characteristics of the thread pool.

What is Context Switching

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext

class MainActivity : AppCompatActivity() {

    private val job = Job()
    private val uiScope: CoroutineScope = CoroutineScope(Dispatchers.Main + job)

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

        // Perform background task without blocking the UI thread
        uiScope.launch {
            showLoading()

            // Switch to the background thread (IO dispatcher)
            val result = withContext(Dispatchers.IO) {
                // Simulate a background task (e.g., network request)
                delay(2000)
                "Background task completed"
            }

            // Switch back to the main thread to update the UI
            updateUi(result)

            hideLoading()
        }
    }

    private fun showLoading() {
        // UI code on the main thread
        textView.text = "Loading..."
    }

    private fun updateUi(result: String) {
        // UI code on the main thread
        textView.text = result
    }

    private fun hideLoading() {
        // UI code on the main thread
        progressBar.visibility = View.GONE
    }

    override fun onDestroy() {
        super.onDestroy()
        // Cancel the coroutine scope when the activity is destroyed
        job.cancel()
    }
}

In the above code snippet, demonstrates context switching between the main thread and a background thread, allowing you to perform background tasks without blocking the UI.

CoroutineScope vs CoroutineContext

  1. A coroutine scope has a single property i.e coroutine context.
  2. Every coroutine needs to run in a specific coroutine scope and several coroutines can be started in the same scope.
  3. If a coroutine is started and some child coroutines are also started in a certain scope, then their job objects will form a parent child hierarchy.
  4. Each of the coroutines in the hierarchy run in the same scope and by default, each coroutine inherits the context of the scope.

In summary,

Please get in touch with us for next part. To be continued..

Happy learning !!!

Share :
Written by:Parita Dey

Interested in Writing Blogs, showcase yourself ?

If you're passionate about technology and have insights to share, we'd love to hear from you! Fill out the form below to express your interest in writing technical blogs for us.

If you notice any issues in this blog post or have suggestions, please contact the author directly or send an email to hi@asdevs.dev.