Overview
Do you often get entangled with asynchronous programming and its callbacks? Welcome, this post is for you. If we list down all the background processes that execute while using an app, we might end up with a book.
Starting from network calls upto fetching any data from a database, all the processes must execute in background/asynchronously as we do not want to block the main/UI thread. We generally use AsyncTask/RxJava to deal with background threads. There is a common problem associated with them: We need callbacks from background tasks to handle the main thread accordingly. This breaks the synchronous execution flow (a common reason to run into blunders).
Fortunately, Kotlin has something called coroutines to make this a lot more simple.
What are coroutines?
They pretty much work as await and async in C#. In simple words, coroutines make asynchronous programming behave like synchronous. The execution on main thread waits for background task to complete before executing next lines of code. As I always prefer practical approach to understand any topic, let me switch to it very quickly.
Practical
People often find using coroutines a bit tricky. I’ll try to represent how to use coroutines on an initial level in as simple way as I can. Let’s consider a common situation : Network calls.
Here’s what I’ll do : I’ll hit an API which returns list of countries with all the details and I’ll list country names with capitals using a recycler view. The thing to look out for is that the code will be written synchronously but the main thread will wait for API to return results before filling out the recycler view.
Note: I’ll use retrofit for handling network calls but in order to stick to the topic, I’ll not get into details of it. I’ll just point out the changes required in the regular process.
Let’s start!
Add gradle dependency for coroutines as follows:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1
Add dependency for retrofit’s coroutines adapter. I’ll get into details of it when it’ll start making more sense.
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
Here, I would like to clear some concepts of coroutines.
Coroutine code is executed only in coroutine scope. We need to add suspend keyword with the function that contains the code. This does the actual magic. It stops a function until another gets executed. Remember, it is not same as block. Blocking a function will stop it while suspending a function pauses it. The following image will help to clear out the confusion, if any.
We’ll use launch( ) method of GlobalScope class of coroutine library which handles all this stuff under a hood. We’ll supply Dispatchers.Main as its argument. This makes sure that the code in the block is allowed to touch the hierarchy of views in main thread.
GlobalScope.launch(Dispatchers.Main) { val request = Retrofit.getRetrofitInstance().getCountries() val response = request.await() pb.visibility = View.GONE response.body()?.let { rv.adapter = Adapter(this@MainAct, it) } } @GET("/rest/v2/all") fun getCountries(): Deferred<Response<List>>
As we need to wait for API call to fetch results, we have to use Deferred object as its return type. Only deferred object have a property to await (request.await( ) here). This is reason we added a dependency for coroutines adapter earlier. It makes retrofit to return the results of Deferred type. Don’t miss out to add call adapter factory while configuring retrofit.
fun getRetrofitInstance(): Service { return Retrofit.Builder() .baseUrl(BASE_URL) .client( OkHttpClient.Builder() .connectTimeout(60, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .build() ) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build().create(Service::class.java) }
Note : If you are dealing with any other async function except network calls and you need Deferred object as return type, you can use anko library. It has something called bg{ } block which might be helpful.
To summarize the process that underwent here, API is hit which returns Deferred object as return type. The main thread waits for it to complete using await( ) method of Deferred object before executing UI operations i.e. hiding progress bar and filling out recycler view.
Here is the output:
You can get your hands on the github repository for this demo using a link attached with this post.
Conclusion
Coroutines are a very powerful tool to make the code sensible, easy to decipher and compact. They can even sometimes help to get rid of boilerplate code. I personally consider them as a very important step in developer-friendly asynchronous programming.
That’s all for now. Thank you for your attention..!