If you are making network requests in your android app then I am pretty sure that you must be using Retrofit to do that. While Retrofit just makes requests it is also important to monitor these requests which can be achieved by using Interceptors.
Retrofit is a wrapper over the OkHttp library which is developed by Square to make the job of making network requests easier.
Why Use Interceptor?
The most real use case I discovered recently when I started my Android Developer Internship. For every new feature, new end points of the product's api has to be made to provide data to the app. Till the api is not ready for use the Android team uses the Interceptor to mimic the network request and provides dummy responses so that development work can continue.
Let's Understand this with a working project. Though I have provided the github repository link at the end but I'll suggest to follow along.
Project Description - Using OkHttp Interceptor to test an api which is not yet available to be used in production by providing test responses for network requests.
Base Url - experimentapi.com End Point - feature
You can provide any Base Url and End Point as the whole purpose of this blog is to use an api which is not available for use yet.
Create a new Android Studio Project. In the app level build.gradle file add the following dependencies
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.2'
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
// Coroutine Lifecycle Scopes
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
Also add the Internet Permission in the Android Manifest file <uses-permission android:name="android.permission.INTERNET"/>
Layout file for the project
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Hello World!"
android:textColor="@color/black"
android:textSize="32sp"
app:layout_constraintBottom_toTopOf="@+id/bt_request"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/bt_request"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_activity" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is a simple layout which contains a button and a textview, we will make a network call by clicking the button and show the data in the textview.
Before proceeding let's first add a test response in the project which the Interceptor will provide after intercepting the request
Follow the steps given to make a directory called raw
In this directory make a file with name testresponse.json
In this file paste the response which you want to provide in the json format. Like this
{
"title": "Feature Title",
"id": "Feature Id"
}
Let's get to the main part of writing our interceptor class
Make a new Kotlin class and name it DemoInterceptor and Inherit it from Interceptor class, implement the members and it should look like this
package com.example.interceptordemo
import okhttp3.Interceptor
import okhttp3.Response
class DemoInterceptor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
TODO("Not yet implemented")
}
}
Our aim is to intercept the network request experimentapi.com/feature and provide the response inside testresponse.json
package com.example.interceptordemo
import okhttp3.Interceptor
import okhttp3.Response
class DemoInterceptor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val uri = chain.request().url.toUrl()
when {
uri.toString()
.endsWith("feature") -> {
// provide test response
}
}
return chain.proceed(chain.request())
}
}
File
testresponse.json
is inside resources directory located in the folder raw and to access it we will need the application context.Also we need to parse the .json file in the response that we are returning.
package com.example.interceptordemo
import android.content.Context
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Protocol
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import java.io.BufferedReader
class DemoInterceptor(private val context: Context) : Interceptor {
private var contentType = "application/json"
override fun intercept(chain: Interceptor.Chain): Response {
val uri = chain.request().url.toUrl()
when {
uri.toString()
.contains("https://www.experimentapi.com/feature") -> {
return getResponse(chain, R.raw.testresponse)
}
}
return chain.proceed(chain.request())
}
fun getResponse(chain: Interceptor.Chain, resId: Int): Response {
val jsonString = this.context.resources
.openRawResource(resId)
.bufferedReader()
.use(BufferedReader::readText)
val builder = Response.Builder()
builder.request(chain.request())
builder.protocol(Protocol.HTTP_2)
builder.addHeader("content-type", contentType)
builder.body(
jsonString.
toByteArray().
toResponseBody(contentType.toMediaTypeOrNull())
)
builder.code(200)
builder.message(jsonString)
return builder.build()
}
}
This is the final DemoInterceptor
class.
builder.code(200) represents that a successful request.
We need a class that maps the our json response
package com.example.interceptordemo
/**
* Data class to for testresponse.json
* */
data class FeatureResponse(
val id: String,
val title: String
)
Let's make our api interface
package com.example.interceptordemo
import retrofit2.Response
import retrofit2.http.GET
interface ExperimentApi {
// api we want to test
@GET("feature")
suspend fun getFeature(): Response<FeatureResponse>
}
Note - suspend keyword tell the compiler that this function will be called from a coroutine only. Discussion about Coroutines is not in the scope of this article
Now what is left is to just make an api call with retrofit, which we will do inside our MainActivity.class.
package com.example.interceptordemo
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.example.interceptordemo.databinding.ActivityMainBinding
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val builder = OkHttpClient()
.newBuilder()
.addInterceptor(DemoInterceptor(applicationContext))
.build()
val experiment_api by lazy {
Retrofit.Builder()
.baseUrl("https://www.experimentapi.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(builder)
.build()
.create(ExperimentApi::class.java)
}
binding.btRequest.setOnClickListener {
lifecycleScope.launch {
Log.e("info", Thread.currentThread().toString())
val response = experiment_api.getFeature()
binding.tvActivity.text = response.body()?.title
}
}
}
}
In the above class:
We first built an instance of our OkHttp client and added our DemoInterceptor to with context to it.
Then we provided our OkHttp client to our retrofit instance.
Lastly we added a click listener to our button which makes a network call by retrofit and puts the data in the textview.
Note - lifecycleScope is used to make sure that main thread doesn't gets blocked while making network request and to manage the lifecycle.
We have successfully completed our project and here is the link of github repository github.com/avidraghav/Interceptor-Demo
If you liked the blog than do comment or react. Any Feedback will be highly appreciated 😄
Connect with me on LinkedIn
Twitter (@avidRaghav)
My Website - avidraghav.dev