Kotlin Training Program

DOWNLOAD APP

FEEDBACK

Basics

Coroutine Scope

Coroutines cannot be launched (started) from anywhere in the code. Instead, they can only be launched from a CoroutineScope.

CoroutineScope is a class that contains functions for launching coroutines.

The simplest way to create a CoroutineScope and enter into the Coroutines world is using the runBlocking() function. It takes a lambda with CoroutineScope receiver as input :

 runBlocking { /* this: CoroutineScope */

}
 

Launch

To launch a Coroutine, use the CoroutineScope#launch() function. It takes a lambda as input parameter in which we write the control flow to be executed in the coroutine.

 runBlocking { /* this: CoroutineScope */
		launch {
				// Write coroutine code here
		}
}
 

launch() function is an example of Coroutine builder i.e. it helps us build and launch a new coroutine. We’ll see another Coroutine builder in later section.

Non-blocking

The launch function does not block the execution i.e. if we have multiple launch blocks, then all coroutines start executing parallelly based on the threads available.

 runBlocking { 
		// Coroutine 1
		launch { }

		// Coroutine 2
		launch { }

		// Coroutine 3
		launch { }
}
 

In the above example, all three coroutines can run parallelly i.e. Coroutine 2 need not wait for Coroutine 1 to finish and Coroutine 3 need not wait for Coroutine 2 to finish.

Nesting

launch() function lambda receives CoroutineScope as receiver :

 launch { /* this: CoroutineScope */

}
 

Hence nested coroutines can be launched from within a coroutine.

 runBlocking { 

		// Coroutine 1
		launch { 

				// Coroutine 1.1
				launch { }

				// Coroutine 1.2
				launch { }
		}

		// Coroutine 2
		launch { 

				// Coroutine 2.1
				launch { }
		}
}
 

Job

launch() function returns a Job object which can be used to manage the launched Coroutine.

Job is a class in Coroutines API that holds the information about a coroutine including its status (Active / Completed / Cancelled) and functions to manage the coroutine.

 val job: Job = launch {
		// Write coroutine code here
}
 

Coroutine Context

When creating a CoroutineScope or launching a new coroutine, we can optionally specify the CoroutineContext.

CoroutineContext is an Interface in Coroutines API that contains set of various elements related to a CoroutineScope such as the CoroutineDispatcher, jobs running in the CoroutineScope and CoroutineExceptionHandler.

 runBlocking(/* CoroutineContext */) { /* this: CoroutineScope */
		launch(/* CoroutineContext */) {
				// Write coroutine code here
		}
}
 

The most basic element of CoroutineContext is the CoroutinesDispatcher. which can be passed as CoroutineContext.

Coroutine Dispatchers

CoroutineDispatcher is a CoroutineContext Element used to specify the thread(s) to be used for execution of coroutine(s).

Types

Following are the available CoroutineDispatchers :

Main

Main Dispatcher schedules coroutines on the single Main thread available to all applications. For UI applications, UI related operations are performed on the Main thread.

IO

IO Dispatcher schedules coroutines on a shared pool of threads (64 by default) for offloading blocking IO tasks such as making network requests, reading / writing file, managing hardware etc. It is suitable for tasks where coroutine might suspends during execution. For example, when waiting for a network request’s response.

Default

Default Dispatcher schedules coroutines on a shared pool of threads (at least 2 and at most equal to number of CPU cores). It is suitable for CPU intensive tasks where coroutine barely need to suspend.

Unconfined

Unlike above three Dispatchers that restrict coroutine execution to a specific thread / thread pool, Unconfined dispatcher does not restrict. Till the first suspension point, caller thread is used and after the coroutine resumes, any available thread may be used.

Usage

For every dispatcher type, constants are defined in the Dispatchers class :

 Dipatchers.Main
Dipatchers.IO
Dipatchers.Default
Dipatchers.Unconfined
 

CoroutineDispatcher is a CoroutineContext Element and it inherits from CoroutineContext so it can be used as a CoroutineContext and passed to runBlocking() or launch() functions to specify the threading policy.

Example :

 // Specify for CoroutineScope
runBlocking(Dipatchers.Default) {
	
}

// OR

// Specify for a specific coroutine
runBlocking {
		launch(Dipatchers.IO) {
				// Executes on IO thread
		}
		launch(Dipatchers.Default) {
				// EXecutes on Default thread
		}		
}
 

Default Disptacher

If CoroutineContext or CoroutineDispatcher is not explicitly specified, then it is inherited from parent CoroutineScope and for outermost CoroutineScope it is Dispatchers.Default.

For runBlocking(), default dispatcher is Dispatchers.Main.

Example :

 // not specified -> Main
runBlocking {
		
		// IO
		launch(Dispatchers.IO) { 

				// not specified, inherited -> IO
				launch { }

				// Default
				launch(Dispatchers.Default) { }
		}

		// Default
		launch(Dispatchers.Default) {

				// Main
				launch(Dispatchers.Main) { }

				// not specified, inherited -> Default
				launch { }
		}
}