16
Networking With Coroutines
Written by Luka Kordić
Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as
text.You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.
Almost every Android app today has some form of network communication. User management, like login and registration, fetching news feed from your backend and uploading a profile picture are just a few of the most common tasks that require you to write networking code. That said, it’s important to keep your UI thread free to do other work while your app communicates with the server. Because networking code is quite common in Android apps, you want to make sure it is:
- Performant - not blocking the main thread
- Easy to read
- Maintainable
- Testable
In this chapter, you’ll see several ways of doing network calls via Retrofit library. First, you’ll make the standard API call with the callback-style approach and Retrofit’s Call
as a return type. Then, you’ll replace that with a much more readable and shorter approach - using the power of coroutines and suspend functions.
Getting Started
For this chapter, you’ll use the same Disney API from previous chapters to obtain a list of characters and present it to the user. To start, open the starter project for this chapter and inspect the code. Focus on the following files:
- DisneyApi.kt in data/networking package
- DisneyApiService.kt in data/networking package
- DisneyActivity.kt in ui/activity package
DisneyApi.kt is the interface to use when creating Retrofit instance. DisneyApiService.kt contains the implementation of the API calls defined in the interface. DisneyActivity.kt represents a simple screen with the basic RecyclerView
setup for displaying a list of Disney characters.
Network Call With Callbacks
To check out the callback-style based implementation, start with DisneyApi.kt file. You’re going to use getCharacters
, which looks like this:
@GET("characters")
fun getCharacters(): Call<CharactersResponse>
fun getCharacters(
onError: (Throwable) -> Unit,
onSuccess: (List<DisneyCharacter>) -> Unit
) {
// Make an asynchronous request
disneyApi.getCharacters().enqueue(object : Callback<CharactersResponse> {
override fun onResponse(
call: Call<CharactersResponse>,
response: Response<CharactersResponse>
) {
// Invoke onSuccess lambda when the results are ready
val data = response.body()
if (data == null) {
onError(Throwable("No response"))
} else {
onSuccess(data.data)
}
}
override fun onFailure(call: Call<CharactersResponse>, t: Throwable) {
// Invoke onError if an error happens
onError(t)
}
})
}
Coroutine-Powered Networking
To see how easy it is to implement coroutines for networking, and how it makes the code more understandable compared with callbacks or other mechanisms, you’re going to refactor the previous example to rely on coroutines. Go back to your DisneyApiService.kt and replace the getCharacters
implementation with this one:
// 1
suspend fun getCharacters(): Result<CharactersResponse> = withContext(Dispatchers.IO) {
try {
// 2
val data = disneyApi.getCharacters().execute().body()
// 3
if (data == null) {
Result.failure(Throwable("No response"))
} else {
Result.success(data)
}
} catch (error: Throwable) {
// 4
Result.failure(error)
}
}
private fun fetchDisneyCharacters() {
lifecycleScope.launch {
apiService.getCharacters()
.onSuccess { showResults(it.data) }
.onFailure { showError(it) }
}
}
Retrofit Meets Coroutines
Here’s a quick recap of what you had to do to switch your API call to use coroutines:
@GET("characters?page=2")
suspend fun getCharactersSuspend(): CharactersResponse
suspend fun getCharacters(): Result<CharactersResponse> = kotlin.runCatching {
disneyApi.getCharactersSuspend()
}
Key Points
- Prefer Kotlin Coroutines over callback-style approach to make your networking code simple and more readable.
- Mark your Retrofit methods with suspend and return the data model you need.
- Retrofit will do the threading for you when you use suspend.
- Make sure to catch any potential errors in the networking layer of the app.
- If you need some metadata about the response, use Retrofit’s
Response<T>
as a return type.
Where to Go From Here?
That’s it for this chapter. It’s a short one, but that’s only because coroutines make networking on Android short and sweet! :]