ππ Retrofit review ππ
This is a spin-off of the βAndroid RxJava in 5 minutesβ article I wrote a while back and has stuff that you might find useful for this one.
You can find it here:
Android RxJava in 5 minutes These go(o)gles do noffing!
What is an API anyway?#
It seems like everyone and their mother is running an API on the internets. If you are not too sure what an API is, or how to consume (????) it no less, fear not. This tutorial will teach you how you too can copy-paste your way to a lead dev position in Silicon Valley.
Cats are important#
Doing something of value is paramount to finding happiness aside from browsing stale memes on r/programmerhumor.
So, of course, we will concentrate on the most important aspect of all, cats (or dogs if you are into that sort of thing).
Retrofit might as well be a synonym for boring and the tutorials out there are really trying hard to be the solution to chronic insomnia so letβs just write something man.

Source code can be found here:
CostaFot/androidβretro-electro Contribute to CostaFot/androidβretro-electro development by creating an account on GitHub.
What you will need#
Just click File -> New project in Android Studio 3 and include Kotlin support, AndroidX artifacts and an empty activity pre-made. Press next on everything. Jetbrains really understands its market (monkeys banging the keyboard like me) and basically writes everything for you these days.
Let the thing finish building.
Dependencies#
Go to the build.gradle (Module: app) file in the dependencies block. It should have these lines in it at least:
// RX Java
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
// Networking
implementation "com.squareup.retrofit2:retrofit:2.4.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0"
implementation "com.squareup.retrofit2:converter-gson:2.4.0"
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
Dependencies
The cat whisperer#
Thereβs loads of public APIs out there to test out.
We gonna go for this one here I found randomly browsing:
TheCatAPI - Cats as a Service, Everyday is Caturday. A public service API all about Cats, free to use when making your fancy new App, Website or Service.
Request an API key and have a look at the docs. Or you can just follow along after you get your key.
Setting things up like you know what you are doing#
Get yourself a generic repository class like this one below.
open class Repository(
baseUrl: String,
isDebugEnabled: Boolean,
apiKey: String
) {
private val apiKeyHeader: String = "x-api-key"
val retrofit: Retrofit
init {
/*adding a logging interceptor when debug is true.
you can check how your API call is going in the LogCat */
val loggingInterceptor = HttpLoggingInterceptor()
if (isDebugEnabled) {
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
} else {
loggingInterceptor.level = HttpLoggingInterceptor.Level.NONE
}
// here's how you can add your api key as a header
val client = OkHttpClient.Builder().addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader(apiKeyHeader, apiKey)
.build()
chain.proceed(request)
}.addInterceptor(loggingInterceptor)
.build()
retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
Generic repository
Reading the docs and going here https://api.thecatapi.com/v1/images/search we get some JSON back.
Put that in the http://www.jsonschema2pojo.org/.
Select:
Source Type : JSON
Annotation style : Gson
Hit preview and you will see an Example class created. This will give you some guidance on the data class you will need so you can convert the response from the server to something of meaning to you.
Here it is anyway:
/**
* The class representing the Json response. Use http://www.jsonschema2pojo.org/ to get this.
* Or you can add this plugin for AS here https://plugins.jetbrains.com/plugin/9960-json-to-kotlin-class-jsontokotlinclass-
* It will create your data class from JSON to kotlin.
*/
data class NetCat(
@SerializedName("id") val id: String,
@SerializedName("url") val url: String,
@SerializedName("breeds") val breeds: List<Any>,
@SerializedName("categories") val categories: List<Any>
) {
override fun toString(): String {
return "NetCat(id='$id', url='$url', breeds=$breeds, categories=$categories)"
}
}
The cat model
Need a file to put the GET request in too.
class CatsDataSource(retrofit: Retrofit) {
private val api: CatsApi = retrofit.create(CatsApi::class.java)
fun getNumberOfRandomCats(limit: Int, category_ids: Int?) =
api.getNumberOfRandomCats(limit, category_ids)
interface CatsApi {
@GET("images/search")
fun getNumberOfRandomCats(@Query("limit") limit: Int, @Query("category_ids") category_ids: Int?): Single<List<NetCat>>
}
}
This is where the GET is located
And the final piece of the puzzle tying these together.
/**
* This guy extends Repository class so the retrofit variable will be available to use as it's instantiated in the init!
*/
class CatsRepository(
baseUrl: String,
isDebugEnabled: Boolean,
apiKey: String
) : Repository(baseUrl, isDebugEnabled, apiKey) {
private val catsDataSource: CatsDataSource = CatsDataSource(retrofit)
// a class to wrap around the response to make things easier later
inner class Result(val netCats: List<NetCat>? = null, val errorMessage: String? = null) {
fun hasCats(): Boolean {
return netCats != null && !netCats.isEmpty()
}
fun hasError(): Boolean {
return errorMessage != null
}
}
// the method that's gonna be called by our activity
fun getNumberOfRandomCats(limit: Int, category_ids: Int?): Single<Result> {
return catsDataSource.getNumberOfRandomCats(limit, category_ids)
.map { netCats: List<NetCat> -> Result(netCats = netCats) }
.onErrorReturn { t: Throwable -> Result(errorMessage = t.message) }
}
}
~The main guy

I lied.
Retro cat#
Get your MainActivity and set up a button so you can test this request yourself.
Since RxJava is being used you gotta be lifecycle aware, hence the compositeDisposableOnPause variable. For more info check the βAndroid RxJava in 5 minutesβ linked at the top_._
The activity:
class MainActivity : AppCompatActivity() {
// Read the docs with detailed instructions to get your API key and endpoint!
// https://docs.thecatapi.com/
// the server url endpoint
private val serverUrl = "https://api.thecatapi.com/v1/"
// this is where you declare your api key
private val apiKey = "yourApiKeyHere"
private val compositeDisposableOnPause = CompositeDisposable()
private var latestCatCall: Disposable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// click listener so you can perform the API call manually
button.setOnClickListener {
getSomeCats()
}
}
// the API call
private fun getSomeCats() {
// initialising the repository class with the necessary information
val catsRepository = CatsRepository(serverUrl, BuildConfig.DEBUG, apiKey)
// stopping the last call if it's already running (optional)
latestCatCall?.dispose()
// perform the API call
// asking for 10 cats. Don't care in what category so just passing null
latestCatCall =
catsRepository.getNumberOfRandomCats(10, null).subscribeOn(Schedulers.io())
.doOnSubscribe {
compositeDisposableOnPause.add(it)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result ->
when {
result.hasError() -> result.errorMessage?.let {
Toast.makeText(this@MainActivity, "Error getting cats$it", Toast.LENGTH_SHORT).show()
}
?: run {
Toast.makeText(this@MainActivity, "Null error", Toast.LENGTH_SHORT).show()
}
result.hasCats() -> result.netCats?.let {
Toast.makeText(this@MainActivity, "Cats received!", Toast.LENGTH_SHORT).show()
}
?: run {
Toast.makeText(this@MainActivity, "Null list of cats", Toast.LENGTH_SHORT).show()
}
else -> Toast.makeText(this@MainActivity, "No cats available :(", Toast.LENGTH_SHORT).show()
}
}
}
// Killing all background threads (if any exist) cause they don't deserve to live when the activity is not running
private fun clearAllJobsOnPause() {
compositeDisposableOnPause.clear()
}
// onPause! Stop everything, the user is probably checking memes elsewhere
override fun onPause() {
clearAllJobsOnPause()
super.onPause()
}
}
Sample activity
Someone might say that all this is really verbose and long-winded.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
Plus, this is a 5 minute tutorial, what did you expect?
Wew lad#
Before you run this make sure to replace the apiKey variable with your own, personal, key.
Give it a run and tap the button. There should be a toast popping up telling you what happened (good or bad). The Logcat will have more information too!
For now, itβs a bunch of links, which donβt seem all that great. Using Glide or Picasso will take care of that problem.
ππ Glide review ππ Or how to load images from the internets when you donβt know what you are doing
Follow on to the next part where we try to introduce the ViewModel and take the logic out of the MainActivity and into the ViewModel.
Retrofit review : The sequel Or how to use MVVM to get some cats so your code is not all over the place
Later.
Smash like and subscribe guys new videos every Wednesday