Search code examples
androidkotlinretrofitretrofit2okhttp

Retrofit SocketTimeoutException Error When API is Down


I'm trying to create an Interceptor in the event that the API I'm using goes down, which has happened when I tried to make an API call on Postman only for it to return a 504 error.

This is the OkHttpClient I have for now. I set it to 5 seconds only for testing purposes.

val client = OkHttpClient.Builder()
    .connectTimeout(5, TimeUnit.SECONDS)
    .writeTimeout(5, TimeUnit.SECONDS)
    .readTimeout(5, TimeUnit.SECONDS)
    .addInterceptor(object : Interceptor {
        override fun intercept(chain: Interceptor.Chain): okhttp3.Response  {
            val response = chain.proceed(chain.request())
            when (response.code()) {
                504 -> {
                    //Show Bad Request Error Message
                }
            }
            return response
        }
    })
    .build()

searchRetrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .baseUrl(URL)
    .client(client)
    .build()

Later in the code, I use Retrofit's execute() method to make a synchronous call to the API. The execute() line and val response = chain.proceed(chain.request()) crashes my app if the API service is down or if it's taking too long to retrieve results. I get the java.net.SocketTimeoutException error.

What can I do to prevent my app from crashing when the API service I'm using is down? What can I add to the Interceptor or should I surround my execute() call in a try catch statement?


Solution

  • Proper solution would be to use enqueue instead of execute. synchronous network calls are almost always a bad idea, because you don't want to block the calling thread. to use enqueue you should do

    call.enqueue(object : Callback<SomeResponse> {
        override fun onFailure(call: Call<SomeResponse>?, t: Throwable?) {
            // This code will be called when your network call fails with some exception
            // SocketTimeOutException etc
        }
    
        override fun onResponse(call: Call<SomeResponse>?, response: Response<SomeResponse>?) {
            // This code will be called when response is received from network
            // response can be anything 200, 504 etc
        }
    })
    

    If you must use execute then at the very least you will have to enclose your execute call in try catch

    try{
        call.execute()
    }
    catch(e: Exception){
         // Process any received exception (SocketTimeOutEtc)
    }