Learning Kotlin is not as hard as you have thought. But the concepts in coroutine might be a little tough in the beginning. There is too much going on with coroutine at a first glance. But here I will make Kotlin coroutine in Android easy with insightful basics which you might find easy. Let us begin with the coroutine.
Before we begin there are a few concepts you must be clear with. Because if you are just starting with coroutine you might confuse your understanding with existing asynchronous or threading concepts.
Something interesting : Install Scrcpy: Mirror Your Android Device Wired or Wirelessly
Quick Navigation
Why do we need coroutine ?
Let me present you with a real world scenario. Sometimes you have to perform a really long operation. That is an API call or could be anything else.
It will take time for your request to come back as a response from server. Let us assume worst and think that it takes somewhere between 5 seconds to 10 seconds.
You cannot let your application sit there just waiting for the request to complete.
Now if you have “But why?” as a question in your mind ?
Because Android application runs on a single thread by default. Anything you do must be taken care by yourself.
Hence, to make sure the user user experience is intact and the code is clean you must extract the logic out.
But coroutine is not thread
Let us first understand that coroutine is not thread for Kotlin. And not the replacement for thread, it kind of is but not really.
You can run an unlimited number of coroutine inside any given thread. And do that without blocking the thread. Hence, the asynchronous behavior.
On the other hand there is a limitation on how many threads can run per application on Android.
Since running multiple threads take space and managing them can be really difficult. We are blessed with the option to use coroutine.
Let us begin understanding the basics of Kotlin coroutine in Android.
What is coroutine ?
Coroutine is a simple block of code that executes without stalling the thread the code is being executed on. It does not wait until the code block is executed.
Rather than waiting for the code to complete it’s execution. It has internal callback hooks. Read more about callback here: Interface in OOP: Guide to Polymorphism and Callbacks.
But the callbacks are much more different and optimized. I have mentioned it just to give you some sense about how coroutine work.
Now that you have a basic understanding of Kotlin coroutine in Android. There are certain concepts tied to coroutine which we must learn before we write any code.
And do note that an unlimited number of Coroutine can be executed on any given thread. Whereas the number of thread an app uses can be limited.
Let us look at them one by one.
CoroutineScope
Before we move into the builders let us see CoroutineScope
first. They are the sub classes of CoroutineContext
. You will understand more about CoroutineContext
after reading about Dispatchers
and rest of the article.
They are basically where your coroutine will be confined to. Each of your coroutine will run in certain context confined to a scope.
You can also use custom scope like:
val myScope = CoroutineScope()
Or use the GlobalScope
or use Android’s multiple scopes for ViewModel
and Activity
or Fragment
. More on this later.
Let us understand what builders are first.
Builders
This is the first thing we are going to look at are coroutine builders. They are the entry point for creating a coroutine.
It tells how exactly you want your coroutine to behave when it gets invoked. There are many ways you can launch your coroutine.
And yes they are just normal methods which run outside the coroutine, or inside. And provides you with the coroutine instance.
Let us look at the common builders.
runBlocking
This is one of the builder in coroutine. This is not used for any of the purposes I have mentioned earlier.
Simply because like the name suggests. Everything inside this block blocks the thread it is running in.
You might someday find a use for it but for now there isn’t any. If you have any use on regular coding journey feel free to mention it on comment below.
Also you can call this from anywhere without even any scope but the rest requires some scope.
Let us look at another one on our list.
runBlocking {
// your suspended methods here
}
Code language: JavaScript (javascript)
launch
This builder creates the coroutine by taking a dispatcher. We will see about dispatcher below. But for now just understand a dispatcher defines where the coroutine will run.
It can be in the background or the current thread or whichever is specified. The dispatcher defines where the code block will execute.
Now that we have understood about launch. Let us see an example
GlobalScope().launch(Dispatchers.IO) {
// your code here
// this code is now asynchronous
}
Code language: JavaScript (javascript)
async
Like the name suggests it performs everything asynchronously. But isn’t that what coroutine should be doing ?
Well yes, but it’s complicated. Coroutine makes your code block asynchronous in various different ways. And async is one of them.
The main difference between async
and launch
is. async returns a Deferred
object whereas launch returns a Job
object.
Its getting really messy right off the bat. But really this is not something to worry about. I will explain them too below.
Where async
really excels is here: it allows the form of parallel processing. You don’t think parallel processing and asynchronous processing is same, do you ?
Think of async like the Promise
in JS if you are familiar with it.
If you have 10 async calls and each of them takes 3 seconds. The total time taken by each call will be 3 seconds and not 30 seconds.
Do you understand what I mean to say ? It’s possible because each calls are performed in parallel.
Let us see a basics example of Kotlin coroutine in Android
GlobalScope().launch(Dispatchers.IO) {
// your code here
val result = async { myApiCall() }
// this line will be executed immediately after previous
// it will not wait 10 seconds
result.await() // you will get the string here
}
suspend fun myApiCall() {
delay(10_000) // 10 second delay emulated for network effect
return "Works!"
}
Code language: JavaScript (javascript)
Takeaway
These are the main builders provided by coroutine. Each have their own unique purpose. Coroutines suspend your methods and resume them back when the result is ready to be presented.
Oh and builders will return a Job instance. Read about Job
s below.
This is what makes coroutine great. Let us see what the dispatchers are first.
Dispatchers
Dispatchers define where the coroutine will run. It’s job is to prepare the context for the coroutine to run on.
There are 3 dispatcher which are
Dispatchers.IO
This is a dispatcher which tells the coroutine to execute the block of code in the background thread.
The codes running here are not compute intensive. Meaning they are not doing complex calculations. They are simply performing IO operations.
Either network or from the disk.
You will mostly be using this dispatcher for your everyday work.
Dispatchers.Main
This is another dispatcher which will tell the coroutine to execute on the MainThread
. In other words the code will run on the MainThread.
It can also be referred to as UiThread
interchangeably. But they are both different in some aspects.
You will be using this dispatcher to update the data from the background to the UI. Like you do it in runOnUiThread { }
, almost identical to that block.
Dispatchers.Default
This is our final dispatcher which will run on a separate thread. Meaning all the CPU intensive tasks should be performed here.
The thread management will be done by the Kotlin itself. You should only worry about your code.
The use of this dispatcher is quite rare. Because you would not normally be doing something intensive everyday. But that might not be the case for everyone.
Takeaway
You must identify which to use and when. You might learn all about coroutine but have very little idea on what it does.
Or how it does things. That should change very early. And with enough research it will change.
Example of Dispatcher
GlobalScope.launch(Dispatchers.IO) {
// your code goes right here
// you can switch IO with Default or Main
}
Code language: JavaScript (javascript)
Let us look at 2 more concepts below.
Job
Job in coroutine are instance which are run in background doing some work. They have states which can be considered more like a life-cycle.
Job can have multiple children and single parent. In normal scenario the cancellation of parent job will cause the child to not execute any further.
The states of the Jobs are listed below.
State | isActive | isCompleted | isCancelled |
---|---|---|---|
New (optional initial state) | false | false | false |
Active (default initial state) | true | false | false |
Completing (transient state) | true | false | false |
Cancelling (transient state) | false | false | true |
Cancelled (final state) | false | true | true |
Completed (final state) | false | true | false |
Each state is just like the in the Android Activity life-cycle. Resumed
, Paused
, etc. You can check the jobs current state with the method provided isActive
, isCompleted
or isCancelled
.
Need in depth info read the official docs here.
For this reason Jobs are extremely helpful when you’re not using async builder. Do you feel we are close to understanding basics of Kotlin coroutine in Android.
Deferred
This is a child class derived from the Job
class. It has it’s own set of methods and states. It works in a similar fashion like jobs.
But use of this lets each of the preceding lines execute before completion of current block. And resumes when the task is complete with the expected result or exception.
What if you want to wait ? Well you can just use .join()
and it will behave like the Job
itself. Meaning it will not go any further without current block execution is complete.
Hence, it allows you do achieve that parallel processing effect gracefully.
Unlike Jobs you don’t have to know much. But if you do here is the official documentation.
Switching the context
In coroutine you can switch the context (Dispatchers) midway. You might have started the coroutine in IO
.
But once your task is complete you might want to update the UI. Which you cannot do if you are not on the main thread. For this purpose you can easily switch context to another Dispatcher.
It is done very easily like so
GlobalScope().launch(Dispatchers.IO) {
// do your long running task here
withContext(Dispatchers.Main) { // and update your UI here }
}
Code language: Kotlin (kotlin)
It is really very easy doing so. And since it is just the basics I have outlined only the bare minimum.
Combining the coroutine context
Now that brings us to another important topic. And that is combining the context.
Let us first understand what a context is. No it is not our normal android.content.Context
class. But it is a coroutine context.
For the sake of understanding you can call the Dispatchers context. If you don’t understand dispatcher you might have skipped the above section. Go back and read that first.
And yes, a dispatcher can be referred to as a context. And you can use +
operator to combine different contexts. But they are very different. This is normally done when handling exceptions which you will see in a little while.
After you use the +
operator you have combined multiple Elements
. As elements is the root of both CoroutineContext
and Dispatchers
. They appear different but you can combine them.
I have just provided the example in the Handling exceptions section but the code will not compile.
Handling exceptions
You execute code inside the asynchronous blocks but what about when you encounter an exception. You cannot wrap code inside try { } catch () { }
and handle it normally.
Except for the business logic part which you have to do it as you normally would. But if some uncaught exception is thrown.
Then there is a standard approach to handling uncaught exception. That is with the help of CoroutineExceptionHandler
.
Let us look at an example:
val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
// your exception handling code here.
}
// somewhere later in the method you do
.launch(Dispatchers.IO + exceptionHandler) {
// your code here
}
Code language: Kotlin (kotlin)
Timeouts
We have handled the exception but if some result are not getting through our coroutine will continue to wait forever. This is almost similar to the network timeout in each API call.
We can simply set timeout like
withTimeoutOrNull(10_000) {
// you can return a null value
}
withTimeout(10_000) {
// doesn't throw a null value
}
Code language: JavaScript (javascript)
If we hit the timeout limit an exception is thrown. There you can handle what to do with it.
Naming coroutine
Yes this is something you can do for debugging purpose. You can assign a unique name with the class CoroutineName("Your Name Here")
.
But what’s the point ? You can use the name to later identify what message was posted from which coroutine.
The usage is really simple .launch(CoroutineName("Name of Coroutine")) { }
. And yes you can use +
and combine it with Dispatchers
and CoroutineExceptionHandler
.
A simple example:
val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
// your exception handling code here.
}
// somewhere later in the method you do
.launch(Dispatchers.IO + exceptionHandler + CoroutineName("Awesome Name")) {
// your code here
}
Code language: Kotlin (kotlin)
Are we done ?
Obviously no. I have not talked about the android dependent version of coroutine. You have coroutine scopes for some android components such as ViewModel
and Activity
. I will write about it soon.
Did not find any follow along code ? Well this is the Kotlin coroutine in android but just the basics. I will provide some in depth Kotlin coroutine example in Android, not just the basics.
Found any mistakes or something isn’t clear ? Feel free to comment below.