Android’s threading model is just like any other UI framework where a single thread (usually called the “UI thread”) is responsible for rendering user interface, capturing events and various other aspects. Performing long running operations such as network requests, DB queries, heavy computation will freeze the UI which throws an “Application not responding” (ANR) error .

Now, to tackle this problem Android already has quite a few abstractions

AsyncTask (Run short operations in sequence, also can be done concurrently) Executors (Running tasks concurrently with fixed thread pool) Intent service (Run long operations in sequence, handled by a queue) Raw threads (For the brave) RxJava (Most popular, not from Android framework)

The above solutions are good, but have several nuances to getting them right. Also, not to mention debugging and cancelling them.

Enter Kotlin Coroutines

Kotlin coroutines is a way of doing things asynchronously in a sequential manner. Creating coroutines is cheap vs creating threads. Reason being

“Coroutines are completely implemented through a compilation technique (no support from the VM or OS side is required), and suspension works through code transformation”

They are still in experimental stages, which means the API will evolve overtime. But, JetBrains has promised to provide backward compatibility for the same. Check here. I’m using them in one of my production apps as well.

Code

To use coroutines, a function has to be marked as suspended. This can be any normal function which is marked as suspended.

Now, to use these functions we need coroutine builder’s which are launch and async

Launch & Async

Launch — Launches a new coroutine without blocking current thread and returns a reference to the coroutine as a Job which can then be used to cancel the coroutine.

Async — Launches a new coroutines and returns its future result as an implementation of Deferred

public actual fun launch(

context: CoroutineContext = DefaultDispatcher,

start: CoroutineStart = CoroutineStart.DEFAULT,

parent: Job? = null,

block: suspend CoroutineScope.() -> Unit

): Job {

val newContext = newCoroutineContext(context, parent)

val coroutine = if (start.isLazy)

LazyStandaloneCoroutine(newContext, block) else

StandaloneCoroutine(newContext, active = true)

coroutine.start(start, coroutine, block)

return coroutine

} public actual fun <T> async(

context: CoroutineContext = DefaultDispatcher,

start: CoroutineStart = CoroutineStart.DEFAULT,

parent: Job? = null,

block: suspend CoroutineScope.() -> T

): Deferred<T> {

val newContext = newCoroutineContext(context, parent)

val coroutine = if (start.isLazy)

LazyDeferredCoroutine(newContext, block) else

DeferredCoroutine<T>(newContext, active = true)

coroutine.start(start, coroutine, block)

return coroutine

}

CoroutineContext — Defines on which thread this coroutine builder runs

DefaultDispatcher — Uses the CommonPool. The developer has full control to specify on which thread it has to run. Other options are UI which runs on the UI thread

CoroutineStart — Defines how it should be started

DEFAULT — Immediately start the execution

LAZY — Start coroutine lazily

Other options are ATOMIC, UNDISPATCHED

Example

Doing things serially

Let’s break it down

Launch creates a new coroutine using UI (Main Thread ) as the Context, which in-turn launches 2 other coroutines which perform some heavy operations in background and return on UI thread

Async creates a new coroutine and returns a deferred on which await can be called upon.

In simple words, the code is executed sequentially where work1 and work2 are executed, then result1 waits for work1 to complete, then result2 waits for work2 to complete.

The result1 and result2 are fetched on a background thread because we used CommonPool as our context which is the default contex

All exceptions are caught with their corresponding try catch block.

Doing things concurrently

The variable result is only available when both work1 and work2 are completed. To do that await() is called on both the deferred objects.

Cancelling a coroutine is simply calling cancel() on a Job.

Retrofit2 + Coroutines

Dependencies

API Interface

Repository

The thing to note is API Interface returns Deferred instead of Call through which we can use it just like any other coroutine.

Conclusion

Coroutines make it easier to write asynchronous code which is more readable making less room for errors and maintenance. Also, not to mention the light-weight nature of coroutines.

Any improvements are always welcomed. Thanks!

P.S

An easy pattern to follow when working with Coroutines as mentioned by Marko Topolnik