Simplifying API consumption to Android Apps from Basic to Advanced — part1: Retrofit

André Figas
3 min readNov 3, 2022

--

If this is not the first page that you found about this content, maybe you found some keywords like Retrofit, this is the main library to handle this kind of Job. Beyond great features, It has noticeable flexibility. Throughout the years, I saw many (Gson, Jackson, RxJava, Coroutines) libraries being released and working perfectly with Retrofit.

Meet mocky.io . There you can create some mocks to proceed with your tests. This is a so powerful tool, that can help you when you have to consume some service that is not ready yet.

Going back to retrofit, do not think you must use some no native library. Natively, you have all that need to do it.

open class UrlResponseTask : AsyncTask<String, Void, String?>(){
override fun doInBackground(vararg params: String): String? {
val url = URL(params.first())
val conn: HttpURLConnection = url.openConnection()
as HttpURLConnection
if (conn.responseCode == HttpsURLConnection.HTTP_OK) {
val `in` = BufferedReader(
InputStreamReader(
url.openStream()
)
)
var page = ""
var inLine: String?
while (`in`.readLine().also { inLine = it } != null) {
page += inLine
}
`in`.close()
return page
}
return null
}
}

This is a simple implementation in that I caught content from a URL.

After you notice is possible to avoid that library, let’s enjoy some features provided by it, then probably the native solutions will sound extremely inefficient to you.

First of all, you import this library. Then go to your build.gradle (project level) and add this entry:

implementation ‘com.squareup.retrofit2:retrofit:2.9.0’

2.9.0 is the current version when this article was written. If you are starting a new project, I recommend you check the last version on the maven repository. This should be applied to all external libraries that I will use in this project.

We have to create an interface to handle our requests

interface Api {
@GET("v3/b09695fe-d653-4b58-837b-aa7f96775ff2")
fun getContent() : Call<ResponseBody>
}

Retrofit requires we specify a request method. To attend to the same use case, I used a GET method. At this point, maybe noticed Is missing the base URL (https://run.mocky.io/). Do not worry, It will be passed at another point.

Retrofit.Builder()
.baseUrl("https://run.mocky.io/")
.build().create(Api::class.java)
.getContent()
.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>,
response: Response<ResponseBody>) {
val message = response.body()?.string()
println("response $message")
}
override fun onFailure(call: Call<ResponseBody>,
t: Throwable) {
t.printStackTrace()
}
})

Here we are releasing the same, but we can do more. At our API interface, each method has to specify the request method. Some common options: GET, POST, DELETE, PUT

Specifically, about our requests, we have some options to send information from our client to our services.

Static URL: That was what you saw in this previous sample

Dynamic URL: Usually some APIs retrieve paginated content, that contains URLs to be consumed in the next request.

interface Api {
fun getContent(@Url url : String) : Call<ResponseBody>
}

Partially Dynamic URL
https://api.github.com/users/andrefigas
For sure, We could not have one method for each possibility, then:

interface Api {
@GET("/users/{id}")
fun getUser(@Path("id") id : String) : Call<ResponseBody>
}

Body

interface Api {
@POST("/users/")
fun getUser(@Body user : User) : Call<ResponseBody>
}

Static Header

interface Api {
@Headers(
"Accept: application/vnd.yourapi.v1.full+json",
"User-Agent: Your-App-Name"
)
@GET("/tasks/{task_id}")
fun getTask(@Path("task_id") taskId: Long): Call<ResponseBody>
}

Dynamic Header

interface Api {
@GET("/tasks")
fun getTask(@Header("Content-Range") contentRange : String)
: Call<ResponseBody>
}

Query:
https://api.open-meteo.com/v1/forecast?latitude=53.50&longitude=14.21

To produce it you have to implement something like that

interface Api {
@GET("/v1/forecast")
fun getWeather(@Query("latitude") latitude : Double,
@Query("longitude") longitude : Double)
: Call<ResponseBody>
}

--

--