OOZOU
Get in Touch
Back to Blog

Handle Complex Network Call on Android with Kotlin Coroutine + Retrofit 2 in MVVM

In this article, I’ll demonstrate how to use coroutine in order to - Chain multiple network request - Create parallel network requests and process the responses when all of them are finished.

July 26, 2018
6 min read
Handle Complex Network Call on Android with Kotlin Coroutine + Retrofit 2 in MVVM

When you’ve been working with some Android applications, you may encounter the scenario where you have to make multiple network requests such as chained or parallel requests and combine their results.

To handle the mentioned scenario, some might say “let’s use RxJava!!!” but… come on the learning curve of RxJava is no joke, at least for me.

Don’t get me wrong, it doesn’t mean that I hate RxJava. RxJava is a really powerful library but Kotlin coroutine seems to suit my existing app architecture better.

In this article, I’ll demonstrate how to use coroutine in order to

  • Chain multiple network request
  • Create parallel network requests and process the responses when all of them are finished.

I won’t get into much detail about how coroutine works but I’ll focus on using it in action.

Introduction

Before we begin I would like to briefly address the concept and the commonly used functions in Coroutine.

Brief Concept

Writing the code asynchronously has always been a pain for me, especially when dealing with complex network requests. It’s hard to write easy-to-read code and sometimes you may end up with a lot of callbacks in your code. Therefore, one of the purposes of Kotlin Coroutine is to help us write a neater code when working with asynchronous programming.

Here is what written in Kotlin Official document.

Coroutines simplify asynchronous programming by putting the complications into libraries. The logic of the program can be expressed sequentially in a coroutine, and the underlying library will figure out the asynchrony for us.

Launch and Async

launch and async are the most commonly used Coroutine builder.

Here is the official definition.

launch- Launches new coroutine without blocking current thread and returns a reference to the coroutine as a Job. The coroutine is canceled when the resulting job is cancelled. async- Creates new coroutine and returns its future result as an implementation of Deferred. The running coroutine is canceled when the resulting object is cancelled.

Take a look at this piece of code as an example.

launch {
  delay(2000)
  println("launch block")
}

val result: Deferred<String> = async {
  delay(3000)
  "async block"
}

println(result.await())

From the example, the difference between launch and async is that async can return the future result which has a type of Deferred<T>, and we can call await() function to the Deferred variable to get the result of the Coroutine while launch only executes the code in the block without returning the result.Coroutine Scope

Coroutine Scope defines a scope for coroutines. Every coroutine builder (like launch, async, etc) is an extension on CoroutineScope. When the scope dies, the Coroutines inside will be out of the picture too. Fortunately, Android lifecycle-viewmodel-ktx provides a really easy way to get a Coroutine Scope in the ViewModel. I will show you how to do so later.

Let’s start!

Dependencies

Here are the things you need to add to your build.gradle

dependencies {
    // Retrofit
    implementation "com.squareup.retrofit2:retrofit:$latestRetrofitVersion"
    implementation "com.squareup.retrofit2:converter-gson:$latestRetrofitVersion"

    // OKHttp
    implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
    implementation "com.squareup.okhttp3:logging-interceptor:$okHttpVersion"
    implementation "com.squareup.okhttp3:okhttp-urlconnection:$okHttpVersion"

    // Kotlin & Coroutines
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"

    // For Viewmodel to work with Coroutine
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleViewmodelKtxVersion"

}

API Interface

Let's create an API interface that defines the API path we going to deal with.

Normally, when defining the path in API interface we’ll do something like this right?

interface SomeApi {    
    @GET("path1/")  
    fun getObject1(): Call<Object1>

    @GET("path2/")   
    fun getObject2(): Call<Object2>   

    @GET("path3/{id}")   
    fun getObject3(@Path("id") objectId : String): Call<Object3> 
}

However, to make it work with Coroutine, we may do it this way.

interface SomeApi {    

   @GET("path1/")   
   suspend fun getObject1(): Response<Object1>

   @GET("path2/")
   suspend fun getObject2(): Response<Object2>    

   @GET("path3/{id}")   
   suspend fun getObject3(@Path("id") objectId : String): Response<Object3> 
}

Basically, we made our API interface’s functions as suspend function so that we can use them asynchronously later on. Providing Retrofit API

object Api {
    fun getApiService(): SomeApi {
        val gson = GsonBuilder().create()
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(provideOkHttpClient(provideLoggingInterceptor()))
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build().create(SomeApi::class.<em>java</em>)
    }

    private fun provideOkHttpClient(interceptor: HttpLoggingInterceptor): OkHttpClient {
        val b = OkHttpClient.Builder()
        b.addInterceptor(interceptor)
        return b.build()
    }

    private fun provideLoggingInterceptor(): HttpLoggingInterceptor {
        val interceptor = HttpLoggingInterceptor()
        interceptor.<em>level </em>= HttpLoggingInterceptor.<em>Level</em>.<em>BODY
        </em>return interceptor
    }
}

Providing Repository

class SomeRepository(val apiService: SomeApi) {

    suspend fun getObject1(): Response<Object1> = apiService.getObject1()

    suspend fun getObject2(): Response<Object2> = apiService.getObject2()

    suspend fun getObject3(objectId : String): Response<Object3> = apiService.getObject3((objectId)

}

Chaining network requests

Let’s say we have to create two network requests in a row and we have to use the result from the first request to create the second one.

In our example, suppose the path of endpoint path3/{id} depends on the result of the endpoint path1/

The code may look like this.

class SomeViewModel(
    private val repository: SomeRepository
) : ViewModel() {
    fun chainingRequest() {
        viewModelScope.launch <strong>{
            </strong>val object1 = repository.getObject1().body()
            val object3 = repository.getObject3(object1.<em>id</em>).body()
            processObject3(object3)
        <strong>}
    </strong>}
}

Create parallel network requests

Suppose we have to create two requests at the same time and process the results when the responses of both endpoints have returned. In our case, let’s call the endpoint /path1 and /path2 parallelly. The code going to look like this.

class SomeViewModel(
    private val repository: SomeRepository
) : ViewModel() {

    fun parallelRequest() {
        viewModelScope.launch {
            try {
                val getObject1Task = async { apiService.getObject1() }
                val getObject2Task = async { apiService.getObject2() }

                processData(getObject1Task.await().body(), getObject2Task.await().body())
            } catch (exception: Exception) {
                Log.e("TAG", exception.message)
            }
        }
    }
}

Let’s break it down.

  • The requests happen at val getObject1Task = async { apiService.getObject1() } and val getObject2Task = async { apiService.getObject2() }. Although the code is written in the sequential, the requests perform asynchronously.
  • Then we pass the results of both requests to be process in successHandler. The results are obtained by getObject1Task.await().body() and getObject2Task.await().body()- The processData will only be called only when both getObject1Task and getObject2Task are finished.

Conclusion

Overall, I’m impressed by how coroutine helps us simplify asynchronous programming. Moreover, It gets us out of the callback hell and makes it easier to handle complex network requests. I think there is no excuse not to give coroutine a try now.

Source

  • https://medium.com/@andrea.bresolin/playing-with-kotlin-in-android-coroutines-and-how-to-get-rid-of-the-callback-hell-a96e817c108b
  • https://proandroiddev.com/kotlin-coroutines-handling-concurrency-like-a-pro-retrofit2-coroutines-31cd0611fd91
  • Kotlin for Android Developers book by Antonio Leiva(https://antonioleiva.com/kotlin-android-developers-book/)

Related Articles

How Can I Design a Software: A Beginners Guide for Business Owners

How Can I Design a Software: A Beginners Guide for Business Owners

Designing software is an exciting yet complex process that involves creativity, logical thinking, and problem-solving skills.

Read More →
The Future of Edge Computing: What Businesses Need to Know

The Future of Edge Computing: What Businesses Need to Know

The digital landscape is evolving rapidly, and businesses must adapt to keep up with rising demands for speed, efficiency, and real-time processing.

Read More →
The Future of Retail: AI, AR, and Digital Transformation Trends

The Future of Retail: AI, AR, and Digital Transformation Trends

Retail is evolving at an unprecedented pace, driven by AI (Artificial Intelligence), AR (Augmented Reality), and digital transformation technologies, revolutionizing the retail industry. Traditional brick-and-mortar stores are no longer just about selling products—they are becoming smart, interactiv

Read More →

Have a Project in Mind?

Let's discuss how we can help bring your ideas to life with our expertise in web and mobile development.

Start a Conversationhello@oozou.com
OOZOU Logo

Bangkok · Singapore · Hong Kong

X
Facebook
Instagram
LinkedIn

Services

  • Web Development
  • AI Agents & Generative AI
  • Mobile App Development
  • Data Analytics & Engineering
  • UI/UX & Product Design
  • Digital Transformation

Company

  • About Us
  • Careers
  • Blog
  • Case Studies
  • Today I Learned
  • Contact

More

  • Industries
  • Partner Network
© 2026 OOZOU. All rights reserved.
Privacy PolicyCode of ConductABAC Policy