Suspend what you’re doing: Retrofit has now Coroutines support!

Paolo Rotolo
ProAndroidDev
Published in
4 min readJun 27, 2019

--

It official now! Retrofit 2.6.0 has been released with support for suspend functions.

This allows you to express the asynchrony of HTTP requests in an idiomatic fashion for the Kotlin language.

Behind the scenes this behaves as if defined as fun user(...): Call<User> and then invoked with Call.enqueue. You can also return Response<User> for access to the response metadata.

To better understand how this works and how to migrate your current code (I know you will, just come back here when you notice how coincise and simple is the new syntax at the end of the post) let’s make an example app that… makes a simple network request!

In this example, we’ll use JSONPlaceholder, a fake REST API that’s very useful when you need a way to quickly test network requests.

We’ll use the /todos endpoint, that returns the json of a simple Todo object.

For reference:

GET https://jsonplaceholder.typicode.com/todos/1

will return:

{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}

Test project set up

First of all, let’s configure Retrofit and the environment for our test. If you’re in an hurry, feel free to skip this part and go directly to the next paragraph.

First the only POJO we need is the data class for a single Todo:

data class Todo(
val id: Int = 0,
val title: String = "",
val completed: Boolean = false
)

Then we write the Retrofit interface. Remember that the fake endpoint is:

GET https://jsonplaceholder.typicode.com/todos/1

So it will be:

Now let’s implement the Retrofit builder that will return our webservice:

Before Retrofit 2.6.0

If you never heard of coroutines and network calls together, here’s a pretty standard way to use Retrofit:

  1. Our Repository makes the actual network request with Retrofit and returns a LiveData with the body of the response

Note that the LiveData value is setted as soon as we have a Response from Retrofit.

We’re not handling errors in this example, but of course you should do so. If you need inspiration, have a look at the NetworkBoundResource implementation on the Andorid Architectural components example repo.

2. The ViewModel simply initializes the Repository, calls getTodo() from it and forwards live data to Activity:

3. The Activity observes live data, waiting for an available value when the network call is completed:

Right before Retrofit 2.6.0

You probably know that in the last months, Retrofit already had “unofficial” support for coroutines. You had to add retrofit2-kotlin-coroutines-adapter by Jake Wharton.

The adapter would convert a Retrofit Call in Deferred, on which you can invoke .await() in a CoroutineScope.

Since this is now deprecated, we’ll skip the implementation.

If you are already using this method, the migration to Retrofit 2.6.0 will be super easy.

In fact, here you can find how they migrated Plaid which was already using the deferred adapter.

<SPOILERS>

Basically it’s a matter of replacing Deferred<>

@GET("api/me")
fun getUser(): Deferred<User>

to suspend

@GET("api/me")
suspend fun getUser(): User

</SPOILERS>

After Retrofit 2.6.0

So the magic now is that you can create suspend methods in your Retrofit interface and directly return your data object.

In our example:

fun getTodo(@Path(value = "id") todoId: Int): Call<Todo>

will become

suspend fun getTodo(@Path(value = "id") todoId: Int): Todo

Repository

At this point, the logic in our Repository will be pretty simple. Seriously.

From this:

To just this:

We don’t need to call enqueue() and implement callbacks anymore! But notice, now our repo method is suspend too and returns a Todo object.

To better understand what we will do next, I invite you to read my previous article about Coroutines and Lifecycle Architectural Components integration.

Viewmodel

Remember that the activity is expecting a LiveData to observe.

LiveData’s building block already provides a Coroutine Scope where to call suspend functions like the one in our repository. So let’s use that with the IO Dispatcher since we’re making a network call.

val firstTodo = liveData(Dispatchers.IO) {
val retrivedTodo = repository.getTodo(1)

emit(retrivedTodo)
}

The building block will automatically switch to the UI thread to update LiveData value when needed.

That’s it. We don’t even need to make a method to get the first todo like before. So our final ViewModel will be:

The Activity code remains untouched as it should be.

As you can see, migrating your networking code to coroutines makes it much more concise and avoids unnecessary boilerplate with the callbacks.

There’re still some issues that are going to be fixed soon with the new implementation. For instance, Retrofit 2.6.0 suspend functions doesn’t support null response body types. This is to avoid an expansive dependency to kotlin-metadata-jvm. Work is being done on this front with a new light parser for Kotlin metadata. You can follow the progress on Github here.

Little off topic, if you are interested of how Retrofit and other Android components work using techniques of metaprogramming, I suggest you to see this very interesting talk: “Mechanisms of Metaprogramming” by Jake Wharton.

Happy coding!

--

--

Android Dev @ Blinkist. Lead @GDG Bari. Pursuing Master in Computer Engineering at PoliBa. Big #OpenSource supporter and #Kotlin fan.