This is just one more example about how in Kotlin we can continue to use the same libraries we’ve always used in Java for Android.
Retrofit is a library that greatly simplifies doing requests to an API, and in this case I’m going to teach you how to integrate it with some LastFM API requests. You can see the full code working in the Bandhook Kotlin repository.
If you want to start today, I recommend you take a look at my free training, where you will have an hour and a half of content to know what are your next steps to become an expert in Kotlin.
Retrofit 2 in Kotlin
The code in Kotlin is going to be very similar to what we would use in Java. We’ll see more in detail what are some of their differences, but you’ll see that everything is pretty easy and intuitive.
And we’ll also create some very useful extension functions, you’ll see.
Also, Retrofit recently can be used in combination with Kotlin Coroutines. If you’re interested in this, please let me know in the comments and I’ll write about it.
Build the build.gradle
I won’t stop here too much, but you need to add the following instructions to build.gradle:
compile "com.squareup.okhttp3:okhttp:$okhttpVersion" compile "com.squareup.okhttp3:logging-interceptor:$okhttpVersion" compile ("com.squareup.retrofit2:retrofit:$retrofitVersion"){ // exclude Retrofit’s OkHttp peer-dependency module and define your own module import exclude module: 'okhttp' } compile "com.squareup.retrofit2:converter-gson:$retrofitVersion"
The first dependencies include the latest version of OkHttp and a logging interceptor, which can be useful for debugging.
The following add Retrofit (excluding OkHttp, so we have control over the version we use), and the Gson converter to convert requests to classes.
Create the communication interface
This is the neuralgic part of Retrofit. It’s where you specify the structure of the requests, which will have to match the API:
interface LastFmService { @GET("/2.0/?method=artist.search") fun searchArtist(@Query("artist") artist: String): Call @GET("/2.0/?method=artist.getinfo") fun requestArtistInfo(@Query("mbid") id: String, @Query("lang") language: String): Call @GET("/2.0/?method=artist.gettopalbums") fun requestAlbums(@Query("mbid") id: String, @Query("artist") artist: String): Call; @GET("/2.0/?method=artist.getsimilar") fun requestSimilar(@Query("mbid") id: String): Call @GET("/2.0/?method=album.getInfo") fun requestAlbum(@Query("mbid") id: String): Call }
It’s quite simple. It identifies the type of the request with the notation, and then the parameters of the request as arguments of the function.
In Retrofit 2, we need to return objects of type Call
.
Initialization of the communication service
First you can initialize the OkHttp client as follows:
val client = OkHttpClient().newBuilder() .cache(cache) .addInterceptor(LastFmRequestInterceptor(apiKey, cacheDuration)) .addInterceptor(HttpLoggingInterceptor().apply { level = if (BuildConfig.DEBUG) Level.BODY else Level.NONE }) .build() }
Here we can see the use of the function, apply
, which will help us initialize the interceptor in the style of a builder, without the need for the class to implement any type of builder.
The LastFmRequestInterceptor
has nothing remarkable, but you can take a look on Github. The creation of the service has nothing different to Java:
val retrofit = Retrofit.Builder() .baseUrl("http://ws.audioscrobbler.com") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() val lastFmService = retrofit.create(LastFmService::class.java)
Make your first request
Due to the need for Call
in Retrofit 2, it becomes a bit more tedious code:
val call = lastFmService.requestAlbums(mbid, name) val result = call.execute().body() val albums = AlbumMapper().transform(result.topAlbums.albums)
However, thanks to the extension functions, we can create a function on Call
to retrieve the values, like this:
val albums = lastFmService.requestAlbums(mbid, name).unwrapCall { AlbumMapper().transform(topAlbums.albums) }
Much simpler, right?
What is the form of unwrapCall
?
inline fun <T, U> Call.unwrapCall(f: T.() -> U): U = execute().body().f()
It’s a function that extends Call
class. It will execute the request, retrieve the body
, and make this one (which will be of type U
) execute the function f()
.
In the above example T
is LastFmResponse
and U
is List
.
Conclusion
With this example I wanted to show you once again that any of the Java libraries you know and love can be used in Kotlin without issues.
Also, far from making things more complicated, in most cases the language will simplify the code.
Still not convinced about Kotlin for Android? Start using it as soon as possible! Thanks to the previous articles you can learn more about Kotlin, or I recommend that you sign up to my free training here.
Could you please show how could we make the networking part as a separate layer?
Could be MVVM or MVP
You have a complete example here https://github.com/antoniolg/Bandhook-Kotlin/
Hello. Thanks a lot for your blog. Can you say anything positive about coroutines adapter? https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter
Thanks!
Probably many things! I haven’t tried it yet, so can’t say… But looks interesting.
how to handle exception ?
That’s a bit outside of the scope of this article, but found this for you: https://futurestud.io/tutorials/retrofit-2-simple-error-handling
Yes i have the same question, im very experienced with Retrofit and overriding the onResponse and onFailure methods of a Call enqueue. How can i override this functionality in Kotlin? For example what happens if the server is down for line: val result = call.execute().body() ? Can i do something like this: val error = call.execute().error() ? The use case would be showing an error message if the albums API call fails