What is Coroutines? How do we use it? and What are its main components?
Note: Kotlin Coroutines is a long topic that we will cover in three articles. So, let’s start…
In this article we will cover the following topics in Kotlin Coroutines:
--------------
Coroutines are a feature in Kotlin that allow developers to write asynchronous and non-blocking code in a more readable and concise manner. They allow you to write sequential, blocking-style code that runs asynchronously, and enables you to easily write and manage asynchronous code, such as network requests or database operations, without the need for callbacks or manual thread management.
----------------------
There are several reasons why you might choose to use coroutines in your Kotlin projects:
Overall, coroutines are a powerful tool for writing asynchronous and concurrent code in Kotlin, and they provide many benefits that make your code more readable, maintainable, and performant.
In Kotlin, you can create coroutines using the suspend keyword and the GlobalScope.launch function.
fun main() { GlobalScope.launch { // this block of code will run in the background delay(1000) println("Hello from coroutine") } println("Hello from main") Thread.sleep(2000) }
In this example, the coroutine is created using the GlobalScope.launch function, which launches the coroutine in a separate background thread. The suspend keyword is used to mark the coroutine as a suspendable function, which means it can be paused and resumed as necessary.
The delay function is a suspension function that waits for the specified amount of time before continuing. In this example, the coroutine waits for 1 second before printing the message “Hello from coroutine”.
Suspended functions are a key concept in coroutines in Kotlin. A suspended function is a function that can be paused and resumed, allowing other work to be done while it is waiting.
Suspended functions are marked with the suspend keyword, and they can be called only from another coroutine or from a function marked with the suspend keyword.
Suspended functions allow you to write asynchronous code that looks and behaves like blocking code. For example, you can use a suspended function to perform a network request, and the function will be suspended while the request is being processed, allowing other work to be done in the meantime.
suspend fun fetchData(): String { delay(1000) return "Data fetched" } fun main() = runBlocking { val data = fetchData() println(data) }
In this example, the fetchData function is marked as a suspended function with the suspend keyword. The function uses the delay function, which is a suspension function that waits for the specified amount of time before continuing. In this case, the function waits for 1 second before returning the string “Data fetched”.
-------------------
Coroutine builders are functions that build and launch coroutines in Kotlin. They provide an easy way to create coroutines and manage their lifecycle.
There are several coroutine builders available in the Kotlin standard library, including:
runBlocking creates a new coroutine and blocks the current thread until the coroutine is complete. It is typically used for testing or for simple use cases where you want to wait for a coroutine to finish before continuing.
fun main() = runBlocking { launch { // this block of code will run in the background delay(1000) println("Hello from coroutine") } println("Hello from main") delay(2000) }
In this example, the runBlocking function is used to create a coroutine that runs in the current thread. The launch function is used to launch a separate coroutine within the runBlocking coroutine. The delay function is used to pause both the main thread and the coroutine for the specified amount of time.
The main thread prints the message “Hello from main” and then waits for 2 seconds using the delay function. During this time, the coroutine is running in the background and will print the message “Hello from coroutine” after 1 second.
After 2 seconds, the runBlocking coroutine will complete and the program will terminate.
launch launches a coroutine in the selected scope. It returns a Job object that can be used to manage the lifecycle of the coroutine.
fun main() { val job = GlobalScope.launch { // this block of code will run in the background delay(1000) println("Hello from coroutine") } println("Hello from main") job.cancel() }
In this example, the coroutine is created using the GlobalScope.launch function, which launches the coroutine in a separate background thread. The delay function is used to pause the coroutine for 1 second, and the println function is used to print a message.
async creates a new coroutine and returns a Deferred object that can be used to retrieve the result of the coroutine when it is complete. The async function is useful for performing asynchronous computations and retrieving the result.
fun main() = runBlocking { val result = async { // this block of code will run in the background delay(1000) 42 } println("Hello from main") println("Result: ${result.await()}") }
In this example, the async function is used to create a coroutine that performs a computation and returns the result. The runBlocking function is used to create a coroutine that blocks the current thread, and the await function is used to retrieve the result of the async coroutine.
The main thread prints the message “Hello from main” and then retrieves the result of the async coroutine using the result.await() function. This function blocks the current thread until the async coroutine is complete and returns the result, which is the value 42 in this example.
withContext allows you to switch the context (thread, dispatcher) in which a coroutine runs. It is used to execute a block of code within a specific context and then return to the previous context.
fun main() = runBlocking { val result = withContext(Dispatchers.Default) { // this block of code will run in the background using the Default dispatcher delay(1000) 42 } println("Hello from main") println("Result: $result") }
In this example, the withContext function is used to switch to the Dispatchers.Default context, which is a dispatcher that provides a default pool of threads for running coroutines. The block of code within the withContext function will run in the background using this dispatcher.
The main thread prints the message “Hello from main” and then retrieves the result of the withContext coroutine. The delay function is used to pause the withContext coroutine for 1 second, and the result of the coroutine is the value 42.
This is a simple example of how to use the withContext function to change the context in which a coroutine runs, and there are many other functions and libraries available for working with coroutines in more complex situations.
------------------
Coroutine Scopes are a way to manage the lifecycle of coroutines and control how they are organized and executed. A scope defines the lifetime of a coroutine and provides a way to cancel or wait for the completion of multiple coroutines.
There are several built-in scopes in Kotlin, including:
fun main() { GlobalScope.launch { // this coroutine will run independently of any other coroutines println("Hello from GlobalScope") } println("Hello from main") runBlocking { delay(2000L) } }
fun main() = runBlocking { val scope = CoroutineScope(Dispatchers.Default) scope.launch { // this coroutine will be cancelled when the runBlocking coroutine is cancelled println("Hello from CoroutineScope") delay(1000L) println("Goodbye from CoroutineScope") } println("Hello from main") delay(2000L) println("Goodbye from main") }
class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) lifecycleScope.launch { // this coroutine will be cancelled when the activity is destroyed println("Hello from lifecycleOwnerScope") delay(1000L) println("Goodbye from lifecycleOwnerScope") } } }
class MyViewModel : ViewModel() { fun doSomething() { viewModelScope.launch { // this coroutine will be cancelled when the viewModel is destroyed println("Hello from viewModelScope") delay(1000L) println("Goodbye from viewModelScope") } } }
That's it for now… We will continue our Coroutines talk in the following two articles.
-------------------
Next Part ==> Kotlin Coroutines (part 2)