In this post, I will show you how to setup auto retrying the request with retrofit Android. For doing that we’ll create an example application that contains auto retrying request functionality with Retrofit. Let’s get started.
Android app your network calls can and will fail randomly due to low bandwidth and low network connectivity. Hence it is a very good idea to add auto-retry policy for some important network calls. It makes for better user experience.
1. Create a new Android Project
Let open the android studio and create a new project with the default template. In this sample app, we are going to use Retrofit and Gson so we have to add dependencies in build.gradle.
implementation 'com.squareup.retrofit2:retrofit:2.7.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
2. Define Retry annotation
Create a @Retry annotation interface. Which APIs annotated with this annotation retry functionally will auto-enable. Here a have set the default attempts is 3 you can change it based on your app need.
package com.retrofitautoretryexample.retrofit; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Documented @Target(METHOD) @Retention(RUNTIME) public @interface Retry { int max() default 3; }
3. Write a retry call adapter factory
Create a new java class named RetryCallAdapterFactory which extends CallAdapter.Factory. In this factory class, we’ll check is request is annotated with @Retry or not. If annotated then every failure request try to call again at least 3 times.
package com.retrofitautoretryexample.retrofit; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import okhttp3.Request; import retrofit2.Call; import retrofit2.CallAdapter; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; public class RetryCallAdapterFactory extends CallAdapter.Factory { private static final String TAG = "RetryCallAdapterFactory"; public static RetryCallAdapterFactory create() { return new RetryCallAdapterFactory(); } @Nullable @Override public CallAdapter<?, ?> get(@NonNull Type returnType, @NonNull Annotation[] annotations, @NonNull Retrofit retrofit) { /** * You can setup a default max retry count for all connections. */ int itShouldRetry = 0; final Retry retry = getRetry(annotations); if (retry != null) { itShouldRetry = retry.max(); } Log.d(TAG, "Starting a CallAdapter with {} retries." + itShouldRetry); return new RetryCallAdapter<>( retrofit.nextCallAdapter(this, returnType, annotations), itShouldRetry ); } private Retry getRetry(@NonNull Annotation[] annotations) { for (Annotation annotation : annotations) { if (annotation instanceof Retry) { return (Retry) annotation; } } return null; } static final class RetryCallAdapter<R, T> implements CallAdapter<R, T> { private final CallAdapter<R, T> delegated; private final int maxRetries; public RetryCallAdapter(CallAdapter<R, T> delegated, int maxRetries) { this.delegated = delegated; this.maxRetries = maxRetries; } @Override public Type responseType() { return delegated.responseType(); } @Override public T adapt(final Call<R> call) { return delegated.adapt(maxRetries > 0 ? new RetryingCall<>(call, maxRetries) : call); } } static final class RetryingCall<R> implements Call<R> { private final Call<R> delegated; private final int maxRetries; public RetryingCall(Call<R> delegated, int maxRetries) { this.delegated = delegated; this.maxRetries = maxRetries; } @Override public Response<R> execute() throws IOException { return delegated.execute(); } @Override public void enqueue(@NonNull Callback<R> callback) { delegated.enqueue(new RetryCallback<>(delegated, callback, maxRetries)); } @Override public boolean isExecuted() { return delegated.isExecuted(); } @Override public void cancel() { delegated.cancel(); } @Override public boolean isCanceled() { return delegated.isCanceled(); } @Override public Call<R> clone() { return new RetryingCall<>(delegated.clone(), maxRetries); } @Override public Request request() { return delegated.request(); } } static final class RetryCallback<T> implements Callback<T> { private final Call<T> call; private final Callback<T> callback; private final int maxRetries; public RetryCallback(Call<T> call, Callback<T> callback, int maxRetries) { this.call = call; this.callback = callback; this.maxRetries = maxRetries; } private final AtomicInteger retryCount = new AtomicInteger(0); @Override public void onResponse(@NonNull Call<T> call, @NonNull Response<T> response) { if (!response.isSuccessful() && retryCount.incrementAndGet() <= maxRetries) { Log.d(TAG, "Call with no success result code: {} " + response.code()); retryCall(); } else { callback.onResponse(call, response); } } @Override public void onFailure(@NonNull Call<T> call, @NonNull Throwable t) { Log.d(TAG, "Call failed with message: " + t.getMessage(), t); if (retryCount.incrementAndGet() <= maxRetries) { retryCall(); } else if (maxRetries > 0) { Log.d(TAG, "No retries left sending timeout up."); callback.onFailure(call, new TimeoutException(String.format("No retries left after %s attempts.", maxRetries))); } else { callback.onFailure(call, t); } } private void retryCall() { Log.w(TAG, "" + retryCount.get() + "/" + maxRetries + " " + " Retrying..."); call.clone().enqueue(this); } } }
4. Create POJO for server response
All retry stuff is done. Meanwhile, I will show you how to use this class in your project. So for doing that create a POJO for parsing server response. So, create a new class named is UserResponse and paste below code.
package com.retrofitautoretryexample.model; import com.google.gson.annotations.SerializedName; public class UserResponse { @SerializedName("authToken") private String authToken; @SerializedName("data") private Object data; @SerializedName("error") private Boolean error; @SerializedName("message") private String message; @SerializedName("statusCode") private Long statusCode; public String getAuthToken() { return authToken; } public Object getData() { return data; } public Boolean getError() { return error; } public String getMessage() { return message; } public Long getStatusCode() { return statusCode; } }
5. Write a UserApiService interface for Retrofit
For integrating with server let’s create a Retrofit Interface and add below code
package com.retrofitautoretryexample; import com.retrofitautoretryexample.model.UserResponse; import com.retrofitautoretryexample.retrofit.Retry; import retrofit2.Call; import retrofit2.http.GET; public interface UserApiService { @Retry @GET("user") Call<UserResponse> getUsers(); }
6. Create a Retrofit Client and add RetryCallAdapterFactory with client
Furthermore, create a retrofit instance that returns UserApiService service class. I have added RetryCallAdapterFactory with retrofit client.
package com.retrofitautoretryexample; import com.retrofitautoretryexample.retrofit.RetryCallAdapterFactory; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class RetrofitClient { private static final String BASE_URL = "https://androidwave.com/api/"; private static Retrofit retrofit = null; public static UserApiService getApiService() { if (retrofit == null) { retrofit = new Retrofit .Builder() .baseUrl(BASE_URL) .addCallAdapterFactory(RetryCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit.create(UserApiService.class); } }
7. Finally, add below code in MainActivity
In this activity, we’ll call get user API and show the result on TextView
package com.retrofitautoretryexample; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.retrofitautoretryexample.model.UserResponse; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class MainActivity extends AppCompatActivity { private TextView txvResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txvResult = findViewById(R.id.txvResults); UserApiService apiService = RetrofitClient.getApiService(); apiService.getUsers().enqueue(new Callback<UserResponse>() { @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) { txvResult.setText(response.body().getMessage()); Toast.makeText(getApplicationContext(), "Success " + response.body().getMessage(), Toast.LENGTH_LONG).show(); } @Override public void onFailure(Call<UserResponse> call, Throwable t) { txvResult.setText(t.getMessage()); Toast.makeText(getApplicationContext(), "Failure " + t.getMessage(), Toast.LENGTH_LONG) .show(); } }); } }
Verify this solution
For verifying this solution, run this app and see you will able to see the result on TextView. Now disconnect the internet from the device and run again. If you check on logcat output will be like this
RetryCallAdapterFactory: Starting a CallAdapter with {} retries.3 RetryCallAdapterFactory: Call failed with message: Unable to resolve host "androidwave.com": No address associated with hostname RetryCallAdapterFactory: 1/3 Retrying... RetryCallAdapterFactory: Call failed with message: Unable to resolve host "androidwave.com": No address associated with hostname RetryCallAdapterFactory: 2/3 Retrying... RetryCallAdapterFactory: Call failed with message: Unable to resolve host "androidwave.com": No address associated with hostname RetryCallAdapterFactory: 3/3 Retrying... RetryCallAdapterFactory: Call failed with message: Unable to resolve host "androidwave.com": No address associated with hostname RetryCallAdapterFactory: No retries left sending timeout up.
Conclusion
With the help of this tutorial, we have learned the implementation Retrying Request with Retrofit. I hope it’s helpful for you, then help me by sharing this post with all your friends who learning android app development.