Android Kotlin

Retrofit Support Suspend Keyword On Functions For Kotlin Android

Kotlin Coroutines are expanding into the areas of Networking. With the new release of Retrofit 2.6.0, we can now add the suspend modifier to our Retrofit Service interface methods without passing any adapter for Deferred type. This allows us to express the asynchrony of HTTP requests in an idiomatic fashion for the language.

This article assumes you have the basic familiarity with Retrofit and Kotlin Coroutines.

Add Dependencies

First, in order to use Retrofit for built-in support suspend keyword, we need to add library in our app-level build.gradle file. Besides this, we also need to add Kotlin Coroutines dependency.

Add the following dependencies into your build.gradle file.

dependencies {
    // retrofit dependency
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    
   // kotlin coroutines dependencies
   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.0-alpha'
   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.0-alpha'
}

Once you’ve add the dependencies hit the Sync Now and everything should be fine without any kind of gradle error.

The Retrofit 2 Service

With the libraries included, now let’s get to the Retrofit service interface definition.

interface ApiService {
   
    @GET(value = "user")
    suspend fun getUserAsync(@Query(value = "id") userId: Long): User
}

We define a service interface called ApiService. Since our only focus on how to define a method with suspend keyword. That’s why I only define a single method matching with the end-point.

Note: If you look at the return type of getUserAsync, you may notice that we’re only returning the User object. Under the hood, Retrofit defines our method fun getUserAsync(.....): Call<User> and then invoked with Call.enqueue on it. Currently in this release of Retrofit 2.6.0 only support non-null response types. Follow this issue for nullable support type.

Perform UserAsync Fetching

Testing the Retrofit suspend method is no different from other functions. Let’s see an example of it.

class MyViewModel : ViewModel() {

    private val job = SupervisorJob()

    private val coroutineContext = Dispatchers.IO + job

    private val serviceApi = // get instance

    fun fetchUser(id: Long) {
        viewModelScope.launch(coroutineContext) {
            val user = serviceApi.getUserAsync("userId" = id)
            // handle fetched user.....
        }
    }
}

The code above is pretty straight forward. If you’re a little bit familiar with kotlin coroutines then you probably heard of  ” The suspend method can only be a call from another suspend block. That’s why we’re calling the getUserAsync method of ApiService inside the launch builder.

Under the hood Retrofit library fetch the User for us and return it. Pretty Simple 👍!

Return Complete Response With Body

You may need to access the response metadata and with the above getUserAsync return type that may not be possible. So, we need to change the return type withResponse<User>.

import retrofit2.Response

interface ApiService {
   
    @GET(value = "user")
    suspend fun getUserAsync(@Query(value = "id") userId: Long): Response<User>
}

Now with new return type we can check what the status code of our request, is the request is successful or not.

class MyViewModel : ViewModel() {

    private val job = SupervisorJob()

    private val coroutineContext = Dispatchers.IO + job

    private val serviceApi = // get instance

    fun fetchUser(id: Long) {
        viewModelScope.launch(coroutineContext) {
            val response = serviceApi.getUserAsync("userId" = id)
            if(response.isSuccessful)
                val user = response.body // handle fetched user
            else if(response.code() == 401)
                 // handle un-authorization
            else
                 //  handle request failed
        }
    }
}

Alright, guys, this was from this article. Hope you learn something, either a little Retrofit 2.6.0 new feature or something about Kotlin.

If you like to explore what’s more came with the new release see the change log on GitHub.

Thank you for being here and keep reading…

6 Comments

  1. Shaheer Malliyil Reply

    This is a great post!

    Do you have a complete example for this ?

    Reason is, I’m getting a weird Retrofit error saying

    java.lang.IllegalArgumentException: No Retrofit annotation found (parameter #2)

    I’ve triple checked my api service syntax & parameters. Funny thing is i only have one parameter in the function.

    So, i debugged the retrofit client creation classes and found out that a null parameter was getting passed into it.

    I’ve been on this for like the last 5 hours! Any guesses ?

    • ahsensaeed067 Reply

      Hey Shaheer,
      Can you show me your ApiService interface? I need to verify that you’re not accepting any Callback’s as a function parameter.

  2. Pavan Bilagi Reply

    Retrofit 2.6.0 suspend functions doesn’t support null response body types. Is there any work around for this ?

    • ahsensaeed067 Reply

      Hey Pavan,
      I did not test this but I think nullable response body is possible with kotlinx.serialization. Here is the sample code.
      interface ServiceApi{

      @POST
      fun login(email : String, password: String) : LoginResponse?
      }

      If you don’t know how to work with kotlinx.serialization check out this article.

    • ahsensaeed067 Reply

      Another thing you can try out if you don’t want to use kotlinx serialization return Response in the retrofit method declaration.

Write A Comment