I am using Retrofit/OkHttp (1.6) in my Android project.
I don\'t find any request retry mechanism built-in to either of them. On searching more, I read OkHttp seems
Just want to share my version. It uses rxJava retryWhen method. My version retries connection every N=15 sec and almost immediately emit retry when internet connection recover.
public class RetryWithDelayOrInternet implements Function<Flowable<? extends Throwable>, Flowable<?>> {
public static boolean isInternetUp;
private int retryCount;
@Override
public Flowable<?> apply(final Flowable<? extends Throwable> attempts) {
return Flowable.fromPublisher(s -> {
while (true) {
retryCount++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
attempts.subscribe(s);
break;
}
if (isInternetUp || retryCount == 15) {
retryCount = 0;
s.onNext(new Object());
}
}
})
.subscribeOn(Schedulers.single());
}}
And you should use it before .subscribe like this:
.retryWhen(new RetryWithDelayOrInternet())
You should manually change isInternetUp field
public class InternetConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
boolean networkAvailable = isNetworkAvailable(context);
RetryWithDelayOrInternet.isInternetUp = networkAvailable;
}
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}}
I don't know if this is an option for you but you could use RxJava together with Retrofit.
Retrofit is able to return Observables upon rest calls. On Oberservables you can just call retry(count)
to resubscribe to the Observable when it emits an error.
You would have to define the call in your interface like this:
@GET("/data.json")
Observable<DataResponse> fetchSomeData();
Then you can subscribe to this Observable like this:
restApi.fetchSomeData()
.retry(5) // Retry the call 5 times if it errors
.subscribeOn(Schedulers.io()) // execute the call asynchronously
.observeOn(AndroidSchedulers.mainThread()) // handle the results in the ui thread
.subscribe(onComplete, onError);
// onComplete and onError are of type Action1<DataResponse>, Action1<Throwable>
// Here you can define what to do with the results
I had the same problem like you and this was actually my solution. RxJava is a really nice library to use in combination with Retrofit. You can even do many cool things in addition to retrying (like e.g. composing and chaining calls).
For those prefer an interceptor to deal with the issue of retrying - Building upon Sinan's answer, here is my proposed interceptor, which includes both retry count and back-off delay, and only retries attempts when network is available, and when request wasn't cancelled. (only deals with IOExceptions (SocketTimeout, UnknownHost, etc.))
builder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// try the request
Response response = null;
int tryCount = 1;
while (tryCount <= MAX_TRY_COUNT) {
try {
response = chain.proceed(request);
break;
} catch (Exception e) {
if (!NetworkUtils.isNetworkAvailable()) {
// if no internet, dont bother retrying request
throw e;
}
if ("Canceled".equalsIgnoreCase(e.getMessage())) {
// Request canceled, do not retry
throw e;
}
if (tryCount >= MAX_TRY_COUNT) {
// max retry count reached, giving up
throw e;
}
try {
// sleep delay * try count (e.g. 1st retry after 3000ms, 2nd after 6000ms, etc.)
Thread.sleep(RETRY_BACKOFF_DELAY * tryCount);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
tryCount++;
}
}
// otherwise just pass the original response on
return response;
}
});
It seems it will be present in retrofit 2.0 from the API Spec: https://github.com/square/retrofit/issues/297. Currently, the best way seems to be catch exception and retry manually.
A solution that worked for me on OkHttp 3.9.1 (considering other answers for this question):
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request();
int retriesCount = 0;
Response response = null;
do {
try {
response = chain.proceed(request);
// Retry if no internet connection.
} catch (ConnectException e) {
Log.e(TAG, "intercept: ", e);
retriesCount++;
try {
Thread.sleep(RETRY_TIME);
} catch (InterruptedException e1) {
Log.e(TAG, "intercept: ", e1);
}
}
} while (response == null && retriesCount < MAX_RETRIES);
// If there was no internet connection, then response will be null.
// Need to initialize response anyway to avoid NullPointerException.
if (response == null) {
response = chain.proceed(newRequest);
}
return response;
}
I have play a lot with this problem trying to find how is the best way to retry Retrofit requests. I am using Retrofit 2 so my solution is for Retrofit 2. For Retrofit 1 you have to use Interceptor like the accepted answer here. The answer of @joluet is correct but he did not mention that retry method need to be called before .subscribe(onComplete, onError) method. This is very important otherwise the request wouldn't be retried again like @pocmo mentioned in @joluet answer. Here is my example:
final Observable<List<NewsDatum>> newsDetailsObservable = apiService.getCandidateNewsItem(newsId).map((newsDetailsParseObject) -> {
return newsDetailsParseObject;
});
newsDetailsObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry((integer, throwable) -> {
//MAX_NUMBER_TRY is your maximum try number
if(integer <= MAX_NUMBER_TRY){
return true;//this will retry the observable (request)
}
return false;//this will not retry and it will go inside onError method
})
.subscribe(new Subscriber<List<NewsDatum>>() {
@Override
public void onCompleted() {
// do nothing
}
@Override
public void onError(Throwable e) {
//do something with the error
}
@Override
public void onNext(List<NewsDatum> apiNewsDatum) {
//do something with the parsed data
}
});
apiService is my RetrofitServiceProvider object.
BTW : I am using Java 8 so a lot of lambda expressions are inside the code.