DEV Community

Cover image for Using Retrofit + Coroutines in Kotlin
Jess Barrientos
Jess Barrientos

Posted on

Using Retrofit + Coroutines in Kotlin

First of all, what is Retrofit?

As its web site says is A type-safe HTTP client for Android and Java ( We are going to use Kotlin...). So, basically is a REST client for Android. It helps us to get and upload data throughout a REST API.

What about coroutines?

On Android, coroutines help to manage long-running tasks that might otherwise block the main thread and cause your app to become unresponsive.

Now, let's take a look at a basic implementation

First, we need to add some dependencies to our project. Starting with adding the version variables in our build.gradle(), under build script

buildscript {
    ext {
    version_moshi = "1.8.0"
    version_retrofit = "2.5.0"
    version_retrofit_coroutines_adapter = "0.9.2"
    version_kotlin_coroutines = "1.1.0"
    version_retrofit_coroutines_adapter = "0.9.2"
    }
    
}
Enter fullscreen mode Exit fullscreen mode

Then add the next dependencies to the build.gradle(:app)

//coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines"
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"

//Retrofit

implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"

//moshi
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
Enter fullscreen mode Exit fullscreen mode

Build your project and now we are ready to start.

In this example, I'm going to use Advice Slip JSON API. More specific https://api.adviceslip.com/advice this URL returns a random advice every time you call it.

Here the JSON we get:

{
  "slip": {
    "id": 45,
    "advice": "Build something out of LEGO."
  }
}
Enter fullscreen mode Exit fullscreen mode

So first we are going to create a new package named “network” in which we are going to add two Kotlin files:

  • AdviceProperty.kt
  • AdviceApiService.kt

In AdviceProperty.kt we are going to add the properties of the JSON in a data-class structure. Like the next example:

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
data class AdviceProperty(
    val slip: AdviceData
    ): Parcelable

@Parcelize
data class AdviceData (
    val id: Int,
    val advice: String
): Parcelable
Enter fullscreen mode Exit fullscreen mode

As you can see, we follow our JSON structure. Our first data-class has a property called slip whose type is AdviceData, this type is another data class with two properties id and advice. This is how we can replicate our nested JSON structure.

Then in AdviceApiService.kt we are going to set up our Retrofit instance.

import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import kotlinx.coroutines.Deferred
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET


// The base URL where our API is
private const val BASE_URL = "https://api.adviceslip.com/"

/* Moshi Makes it easy to parse JSON into objects 
you can use GSON instead if you want*/

private val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

//Here is our retrofit instance

private val retrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .addCallAdapterFactory(CoroutineCallAdapterFactory())
    .baseUrl(BASE_URL)
    .build()

//Then we are going to create the interface 
which is going to help to handle our GET method 
to call the API*/

interface AdviceApiService{

    @GET("advice")
    fun getProperty():
            Deferred<AdviceProperty>
}

/* Singleton to create this instance only once 
and get it ready every time we call it. */

object  AdviceApi {
    val retrofitService : AdviceApiService by lazy {
        retrofit.create(AdviceApiService::class.java)
    }
}
Enter fullscreen mode Exit fullscreen mode

Alright!, we're almost done!

I'm using the MVVM Architecture, which means for each fragment/activity I have a ViewModel Class in which I manage all the data for my app, and also this API call.

So in the ViewModel (this can be your class), I have a variable named _properties which is going to store my data, you can see its type it's AdviceProperty (Remember our class in the network package)

private var _properties = MutableLiveData<AdviceProperty>()
Enter fullscreen mode Exit fullscreen mode

Next, I have other two variables:

private val viewModelJob = Job()
private val coroutineScope = CoroutineScope( viewModelJob + Dispatchers.Main )
Enter fullscreen mode Exit fullscreen mode

The first one is the Job which is gonna define the lifecycle of the coroutine scope. So if its children fail, its gonna notify the parent and it will cancel the other children in case we have more instances running.

The second one is our coroutineScope, here we define in which thread our coroutine is going to run.

Then the method who is going to create the coroutine, and also manage the call to the API: getAdviceProperty

private fun getAdviceProperty(){

    coroutineScope.launch { 

        var getProperty = AdviceApi.retrofitService.getProperty()

        try {
            _properties.value =  getProperty.await()

        }catch (e: Exception){
            //library to show logs
            Timber.i("EROR ${e}");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see here, we have coroutineScope.launch this block of code allows us to create a new coroutine and run it in the thread we define previously.

Then we can see in the getProperty variable the word “await”, this is because our Singleton class returns a Deferred object (feature or a promise in java).

If you are using view models do not forget to cancel the Job in the onClear() method

override fun onCleared() {
    super.onCleared()
    viewModelJob.cancel()
}
Enter fullscreen mode Exit fullscreen mode

And that’s it! We have now our response in a variable and we can use it wherever we want. It is much less code than we use to write with java or without coroutines.


Take a look at my repository: lucky Piñata It has a working example of this topic.

Top comments (0)