Android Coroutine Guideline: Introduction
This is the first blog-post of the coroutine series. I know lots of coroutine guidelines, blog posts, and videos on the internet. This is going to be yet another series. More coroutine doesn’t kill anyone. :)
Before we jump into the Coroutines, I want to step back and look at the existing concepts that we are using.
We always write code that executes and returns the value. If we are lucky, the method does its job and returns immediately but generally, we are not. We always wait for the result. This is why we use threads in our projects.
Concurrency is one of the most complex topics. Maybe the hardest one. In the Android world, we need to create a thread for a long-running operation to not block the main thread. When it is the fire and forget method and we can ignore the result, communication between threads won’t be a problem for us. But generally, we care about the result. I mean, we wait for the result. When that is the case, we create a handler to post the result value from the background thread to the UI thread.
Even for the basic operation, we need to write that boilerplate. Think about that, you just want to execute the request and show the result in the simple text. There should be a better option. Let’s go over the sending a request and look at the existing options we have.
Most of the HTTP Clients for android works async and thread-safe for Android. They expect a listener to invoke when their operations are completed. We don’t need to write thread handling and thread-communication. They use threads under the hood and invoke the listener that we passed as a function parameter.
But we tend to write sequential code. This code is also preferable. It is all OK. The product team can come and ask you to show the last date the user was online in UI which the backend team is already developed recently. No problem. We can add another callback with another request, so on.
As I said, we tend to write sequential code but this code gets more messy and ugly. Like the hell. Callback hell. End of the day, we need to find another solution. The callback way doesn’t work for us in the long run.
3rd Party Library
There are 3rd party libraries like RxJava to execute sequential long-running operations. Let’s see how can we achieve the same code basically.
Yes, this is a better approach than callbacks. At least we are a step closer to sequential code. This code has downsides too. We need to memorize all of the mapper method names when we want to code. And also we don’t have direct access to the userInfo data in the following mapper methods.
Can we not deal with long-running operations by writing a natural way. Like this.
What we really want to:
- We need concurrency, this is inherent.
- We need main-safe methods.
- We want to write code sequentially just like the above one.
We can not solve our problem with existing technologies. That is where coroutine comes to the stage.
Coroutines are light-weight threads. (I know you have been exposed to this sentence too much.) What we mean by light-weight is, we can create coroutine as many as we want but we can not create an infinite thread in our applications. A single thread occupies about 2 megabytes of memory. Our device has limited memory. So with coroutines, we mostly don’t care about this.
There are some keywords in a coroutine that are very interconnected by describing semantically. I will show you a little example and describe them one by one. I am a little bit relax because I will not try to fit all coroutine content into this blog-post. This will be a coroutine series as I mentioned above.
Let’s see what these keywords are basically.
Basic Coroutine Sample
Your data source method has the following methods
And your activity has the following
There are some keywords here and you have no idea what they about. Bear with me. One step at a time.
Note: Don’t use GlobalScope on your production code. I used it only for simplicity. We will talk about it later but If you are curious to know the reason, here is the post that Roman Elizarov talked about it.
Suspend keyword is literally does the same thing with the name. A coroutine can understand that this method is suspendable while executing. So coroutine will suspend this method and resume when it is completed its job. Don't’ worry. It will take some time to grasp it completely. When you look at the compiled code, you will see the kotlin generates a method with Continuation callback. So we can say that the suspend keyword hides the callback logic from us. It provides us to write sequential code and handle all messy callback itself under the hood.
The suspending functions can only be called from another suspending function or coroutine scope.
As you see above, we wrote sequential code inside fetchData() method which is also a suspending function. You can’t imagine how powerful the suspend keyword is.
We have our suspending function. Now we can start our coroutine. There is a couple of ways to start a coroutine. launch is one of them. We will examine others later on.
Launch: starts a coroutine.
Dispatchers.Main: We are informing the coroutine that we want to execute the scope in the main thread. (Until we switch to another context.)
withContext(Dispatchers.IO): You can switch to the desired context in your code.
We will cover Dispatchers in the next blog-posts
This is the basic introduction to coroutines. I know too many different words we are not familiar with but we will deep dive into every piece on the following series. I just wanted to give a brief introduction to coroutines in this post. I know you can’t convert your existing code to coroutine by reading this post. (Please don’t do it right now.)
What we learned in this blogpost;
- Coroutines are light-weight threads. So you can create as many coroutines as you want. You can not do this with threads.
- Suspend functions are playing a really important role in coroutines. A coroutine can suspend the function and resume code when the function is finished its job.
- We can switch to any context by using withContext(). (We have Dispatchers.IO, Dispatcher.MAIN, Dispatcher.DEFAULT, Dispatchers.UNCONFINED)
- We can launch a coroutine with the launch coroutine builder.
PS. This is just a preparation before we deep dive.
Thanks for reading.