Retrofit 2: Catch connection timeout exception
For Retrofit 2
Define a listener in your web service instance:
public interface OnConnectionTimeoutListener {
void onConnectionTimeout();
}
Add an interceptor to your web service:
public WebServiceClient() {
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(10, TimeUnit.SECONDS);
client.setReadTimeout(30, TimeUnit.SECONDS);
client.interceptors().add(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return onOnIntercept(chain);
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
webService = retrofit.create(WebService.class);
}
Enclose your intercep code with try-catch block and notify listener when exception happens:
private Response onOnIntercept(Chain chain) throws IOException {
try {
Response response = chain.proceed(chain.request());
String content = UtilityMethods.convertResponseToString(response);
Log.d(TAG, lastCalledMethodName + " - " + content);
return response.newBuilder().body(ResponseBody.create(response.body().contentType(), content)).build();
}
catch (SocketTimeoutException exception) {
exception.printStackTrace();
if(listener != null)
listener.onConnectionTimeout();
}
return chain.proceed(chain.request());
}
None of the answers quite worked for me but they led me in the right direction. See below how I did it in Kotlin.
You can either throw the exceptions in the ErrorInterceptor
and catch them in your api call function:
class ErrorInterceptor : Interceptor {
override fun intercept(chain: Chain): Response {
val request = chain.request()
try {
val response = chain.proceed(request)
val bodyString = response.body!!.string()
return response.newBuilder()
.body(bodyString.toResponseBody(response.body?.contentType()))
.build()
} catch (e: Exception) {
when (e) {
is SocketTimeoutException -> {
throw SocketTimeoutException()
}
// Add additional errors... //
}
}
}
Or bundle exceptions with a response object; something like this:
class ErrorInterceptor : Interceptor {
override fun intercept(chain: Chain): Response {
val request = chain.request()
try {
val response = chain.proceed(request)
val bodyString = response.body!!.string()
return response.newBuilder()
.body(bodyString.toResponseBody(response.body?.contentType()))
.build()
} catch (e: Exception) {
var msg = ""
val interceptorCode: Int
when (e) {
is SocketTimeoutException -> {
msg = "Socket timeout error"
interceptorCode = 408
}
// Add additional errors... //
}
return Response.Builder()
.request(request)
.protocol(Protocol.HTTP_1_1)
.code(interceptorCode)
.message(msg)
.body("{${e}}".toResponseBody(null)).build()
}
}
}
Add the ErrorInterceptor
to your okHttpClient
:
okHttpClient.newBuilder()
.addInterceptor(ErrorInterceptor())
.connectTimeout(10, TimeUnit.SECONDS)
// ... //
.build()
And then something like this in your repository layer:
suspend fun makeAPIRequest(): Resource<ApiResponse> {
return withContext(ioDispatcher) {
var response: Response<ApiResponse>? = null
try {
response = getResponse()
// Do additional ops on response here //
} catch (e: Exception) {
// Exceptions thrown in ErrorInterceptor will propagate here
}
}
}
@Override
public void onFailure(Call call, Throwable t) {
if(t instanceof SocketTimeoutException){
message = "Socket Time out. Please try again.";
}
}
In case someone come here with Kotlin/Coroutines facing the same issue, add an error handler to your coroutines scope:
CoroutineScope(Dispatchers.IO).launch(handler) {
while the handler by itself looks like:
val handler = CoroutineExceptionHandler { _, exception ->
Log.t("Network", "Caught $exception")
}