Network Calls: Kotlin Coroutine + Retrofit 2

10 Nov 2019
4 min read

Concurrency, Consistency, Parallel Processing: multiple names all suggesting the same requirement for simple task for computation of different task all at the same time and ensuring the expected output.


A Little Background

Lets get into the Android Realm, where asynchronous code is at the core of the Android Development. Every network request should be on background thread and later the respective response must be handled on the Main/UI thread. This whole operation or “monkey business” used to need a good amount of boiler plate to setup and if you have a descent size project this can go havoc in few weeks. Enough of background story, let discussion various ways to achieve the same.

  • AsyncTask — Run short operations in sequence, also can be done concurrently. (Boilerplate code, Tedious cancellation and failure handling)
  • Executors — Running tasks concurrently with fixed thread pool. (No direct access to UI thread, Needs micro managing)
  • Intent service — Run long operations in sequence, handled by a queue. (No direct access to UI thread by default, Tasks are queued)
  • RxJava — Using Observer and Observable for handling stream of data. (Steep learning curve, Mainly a event processing library)

New Kid on the Block: Coroutine

Corountines in Kotlin allow us to deal with asynchronous tasks in a sequential/imperative way. Using coroutines is cheaper than using threads and managing them is way easier and more straightforward. Beginning with Kotlin 1.3, coroutines have become available in stable versions.

We are not gonna cover coroutines in depth in this session. Many blogs explaining them neatly can be found around internet [this blog is inspired from many of them 😜]

Setting up gradle

Add the following dependencies to build.gradle at app level module.

dependencies {
    // Retrofit
    implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
    implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
    implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
    // OKHttp
    implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
    implementation "com.squareup.okhttp3:logging-interceptor:$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"
}

Coroutine returns a future object an object that holds the values that are populated at a later point in time called Deferred. This are similar to other techniques for storing value of async operations like Java’s Future or Javascript Promise.

For working with Retrofit, we need special CallAdapter [that returns the values from network calls] provided by Kotlin Coroutine Adapter.

Defining API Interface for Retrofit

interface APIInterface {
        @GET("/hello")
        fun getEmployeeListing(): Deferred<Response<EmployeeListModel>>
    }plementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
}

We are returning the Deferred object of the User defined type in this case EmployeeListModel.

Configuring Retrofit API

class APIService {
    private lateinit var apiInterface: APIInterface
    fun getClient(): APIInterface {
        val gson = GsonBuilder().create()
        val retrofit = Retrofit.Builder()
                .baseUrl("https://limitless-lake-93364.herokuapp.com")
                .client(provideOkHttpClient(provideLoggingInterceptor()))
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .build()
        apiInterface = retrofit.create(APIInterface::class.java)
        return apiInterface
    }
    private fun provideOkHttpClient(interceptor: HttpLoggingInterceptor): OkHttpClient {
        val b = OkHttpClient.Builder()
        b.addInterceptor(interceptor)
        return b.build()
    }
    private fun provideLoggingInterceptor(): HttpLoggingInterceptor {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        return interceptor
    }
}

Using the Dispatchers.IO the api is invoked on a background thread, then we await for the response without using the any callback.

Handling UI updates On Main thread

GlobalScope.launch(Dispatchers.Main) {
  val resultList = mappingResult(response?.employee)
  pro_bar.visibility = View.GONE
  activity?.let {
      val adapter = ArrayAdapter<String>(it,
              android.R.layout.simple_list_item_1, resultList)
      list_api.adapter = adapter
  }
}

Using the Dispatchers.Main will render the UI with the response on the UI/Main thread without any further handling.

Conclusion

Using Kotlin coroutine with Retrofit makes the code more readable. Making Asynchronous Code callback free in a very neat concise way. Now as it is available on stable version plus integration with network libraries like Retrofit, managing large Code base will be more trouble-free.

Working example is able on my Github repo.

For in depth overview on Kotlin Coroutines you can watch my talk on basic setup and other implementation on the same – here!

Discover more from Tech Shaadi

Subscribe now to keep reading and get access to the full archive.

Continue reading