A coroutine is a concurrency design pattern that you can use on Android to simplify code that run asynchronously. Coroutine is not a thread but its functionality is same like a thread. Coroutine have several function and  builder lets go through this.

Suspending functions

A suspending function is just a normal function which have an additional suspend modifier which indicate that function can suspend the execution of a coroutine. here is point that must be take care of that a suspending function are only allowed to be called from a coroutine or from suspending function. Here is the example of suspending function:-

suspend fun delay(timeMillis: Long) {...}
suspend fun someNetworkCallReturningValue(): SomeType {
 ...
}

Suspending functions have ability to suspend the execution of the present coroutine but not block the current thread or without blocking the present thread. However, it doesn’t say anything about what the present thread will neutralize the meantime. All of this is often controlled by how your suspending function is termed by the non-suspending functions world, but there’s nothing asynchronous about suspending functions from the user’s perspective. Suspending functions are only asynchronous if they’re explicitly used intrinsically. And keep remember in  mind that calling them implicitly splits your functions into sub-tasks, without fear yet about the intricacies of threads and dispatching. That’s actually why they’re great, you don’t must worry this when you’re inside.

You have noticed that suspending functions don’t have any kind of special return types. they’re really declared similar to usual functions. We don’t require any wrapper class like Java’s Future or JavaScript’s Promise. This confirms that suspending functions don’t seem to be asynchronous themselves unlike JavaScript’s async functions, which return promises.

Coroutine builders

Coroutine builders are group of normal functions which will create a new coroutine which help to run a given suspending function. we are able to call coroutine builder from normal non-suspending functions because they’re not suspending themselves, and seems as a bridge between the simple and therefore the suspending world.

The Kotlinx Coroutines library have multiple coroutine builders to try and do various things. we are going to see some within the following subsections.

RunBlocking

Run-blocking  allow us to deal the suspending function from the normal function by block the current thread and wait in a very simple way. The coroutine builder which block the current thread is called runBlocking :

fun main() {
println("Hello,")
// here we will create a running coroutine that provided suspending lambda
// and it will block the main thread while it waiting for the coroutine to finish it’s current execution
runBlocking {
// now we are inside a coroutine
delay(2000L) 
// it will suspends  coroutine for 2 seconds
}
// will be executed after 2 seconds
println("World!”)

In this we can see that, the suspending function which we have given and its children within the call hierarchy will effectively block currently executing thread until it finishes executing.

fun <T> runBlocking(
   ..., 
   block: suspend CoroutineScope.() -> T
): T {
  ...
}

This is mostly used from the main() function and help to give a sort of top-level coroutine from which you have to work, and keep the JVM alive while doing so.

Launch

mainly, the purpose of coroutines is not like to block the thread, the purpose is rather start an asynchronous task. The coroutine builder used launch which help to start a new coroutine in background and keep working in the meantime. Let’s have this example:

fun main() {
GlobalScope.launch { // 
it will launch new coroutine in background and continue
delay(1000L)
println("World!")
}
println("Hello,") // main thread continues here immediately
runBlocking {     // but this expression blocks the main thread
delay(2000L)  // ... while we delay for 2 seconds to keep JVM alive
}
}

Async

It launches new coroutine without blocking the current thread. Async builder inherit thread and coroutine scope of immediate parent coroutine and return a reference to the Deferred <T> object. With the use of deferred object you can cancel the coroutine, wait for coroutine to finish or retrieve the return result.

fun main() {
val deferredResult: Deferred<String> = GlobalScope.async {
delay(1000L)
"World!"
}
runBlocking {
println("Hello, ${deferredResult.await()}")
}
}

The coroutineScope builder

Kotlin avoid thread blocking functions inside coroutine so due to this the use of runBlocking is discouraged from inside coroutines and allow suspending operation instead of this. The suspending function is equivalent of runBlocking is the coroutineScope builder. CoroutineScope suspends the currently working coroutine until all it’s child coroutines have finished their execution. Here is the example:-

fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking")
}
coroutineScope { // Creates a new coroutine scope
launch {
delay(500L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") //  before nested launch
}
println("Coroutine scope is over") // after nested launch
}

Using Dispatchers With Kotlin Coroutines

If you want to execute a coroutine using different CoroutineDispatchers then you can execute with some of the available CoroutineDispatchers like: Dispatchers.Main, Dispatchers.IO and Dispatchers.Default.Here are the some dispatchers:

  • Dispatchers.Default: It is mostly use for CPU-intensive work, like if we want to sorting large lists, doing complex calculations and similar. Dispatchers.IO: It is use for networking or reading and writing from files. In some other words any input and output kind of work we can do with the help of this, as the name states
  • Dispatchers.Main: It is use for performing UI-related events. Like, of you want to showing lists in a RecyclerView, updating Views and so on.

To switch between the main and background thread  you can use some of these dispatchers.

Scoping Kotlin Coroutines

Now, if you want to define the scope when the coroutine runs, you have to use a custom CoroutineScope to handle the lifecycle of the coroutines. To do so we have to declare a property and initialize it under the parent Job as we can see in example:

private val coroutineScope = CoroutineScope(Dispatchers.Main + parentJob)

The plus() operator helps you create a group of CoroutineContext elements, which you come with the coroutines in an exceedingly particular scope. The contexts and their elements are a group of rules each Kotlin Coroutine has got to adhere to.

Here is some set of elements can have information about:

  • Dispatchers, help to dispatch coroutines in a particular thread pool and executor.
  • CoroutineExceptionHandler, help you to handle thrown exceptions.
  • Parent Job, With the help of this you can use to cancel all Kotlin Coroutines within the scope.

Internal Workings of Coroutines

Internally, Kotlin Coroutines use the concept of Continuation-Passing Style programming, also called CPS. This kind of programming involves passing the control flow of the program as an argument to functions. This argument, in Kotlin Coroutines’ world, is thought as Continuation.A continuation is nothing over a callback. Although, it’s way more system-level than you standard callbacks. The system uses them to understand when a suspended function should continue or return a worth.

For example, after you call await(), the system suspends the outer coroutine until there’s a worth present. Once the worth is there, it uses the continuation, to return it back to the outer coroutine. Hopefully, it now makes a small amount more sense, and it doesn’t seem all that magical! Another really important thing to grasp, in Kotlin Coroutines, is that the way exceptions are handled.

Handling Exceptions

Exception handling in Kotlin Coroutines acts differently depending on the CoroutineBuilder which you are using. Here’s how exceptions behave for the builders you utilized in your code and the way how to handle them:

  • launch: The exception appear to the parent and can fail your coroutine parent-child hierarchy. This will throw an exception immediately in the coroutine thread. To avoid these exceptions we can use  try/catch blocks, or a custom exception handler.
  • async: You put exceptions until you consume the result for the async block. which means if you forgot to utilize the results of the async block, with the help of await(), you’ll not get an exception at all! The coroutine will bury it, and your app are going to be fine. If you would like to avoid exceptions from await(), use a try/catch block either on the await() call, or within async().

Let’s see an example to handle exception

Create a property, coroutineExceptionHandler, as follows:
// 1
private val coroutineExceptionHandler: CoroutineExceptionHandler =
    CoroutineExceptionHandler { _, throwable ->
      //2
      coroutineScope.launch(Dispatchers.Main) {
        //3
        errorMessage.visibility = View.VISIBLE
        errorMessage.text = getString(R.string.error_message)
      }

      GlobalScope.launch { println("Caught $throwable") }
    }

Creating new coroutines

To create a new coroutine you have a CoroutineContext and when Once you have a CoroutineContext, you can use the these methods launch() or async() to creat a new coroutine

  • launch() — when want to perform some data computation and login operation and it can not be run in background. it return of job object by using this you can cancel the coroutine or wait for coroutine to finish.
  • async() — It create a new coroutine, and allows you to return a result with a suspend function called await().

As we know that a regular function is not able to call the suspend function, await() — usually it makes sense to start the coroutine with launch().Every coroutine you create:

  • returns a Job that can be used to manage its lifecycle.
  • inherits the CoroutineContext from its parent (another coroutine or the CoroutineScope) and overrides the context parameters if you specify any.

Making coroutines cancelable

To Cancel a coroutine it should be cooperative: the code running won’t stop by its own.Suspend functions from kotlinx.coroutines (withContext() for example) are cancelable, for other functions — you’ll check manually if the coroutine was canceled within the following ways:

  • job.isActive — In this you can perform other actions when you are canceling coroutine but it requires an if statement.
  • job.ensureActive() — throws a CancellationException immediately.
  • CoroutineScope.yield() — if you wish to permit the thread to try to to other work without having to feature more threads to the pool. the primary operation done by yield will check for completion and throw a CancellationException if the duty isn’t active (isActive=false).

A good place for yield() or ensureActive() will be the beginning of every cycle of a work-heavy loop.

You may also like...

0 Comments

No Comment.