Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Good approach to manually retry requests in Retrofit Android

I understand retrofit automatically retries on failures, but I want to check for a specific error. If the error is due to a certain HTTP code, I need to retry that request after modifying it.

This can be easily done using synchronous calls, but I am making an asynchronous call (by passing callbacks). When I get the error in the callback, I want to retry the request - but all I have is the RetrofitError object (and I have lost the request body).

I have multiple requests going on from the same activity (concurrently) and so I am avoiding saving all requests and nullifying them on success.

Is there a better way to achieve this requirement ?

like image 240
dev Avatar asked May 01 '14 23:05

dev


2 Answers

If you are using OkHttp as your HttpClient and have updated to Retrofit >= 1.9.0 then you can use the new Interceptor. Specifically, an Application Interceptor will let you retry and make multiple calls.

You can see example pseudocode I posted on a similar question for handling expired tokens.

Also note, Retrofit 2.0 will have Interceptors according to this GitHub ticket. That will remove the dependence on OkHttp, though I still recommend using it.

like image 63
theblang Avatar answered Oct 16 '22 04:10

theblang


Here's what you could try. First the network request method:

void getDataFromServer(final int dataId) {
     ...

    dataService.getDataFromServer(dataId, new Callback<ResultData>() {
        @Override
        public void success(final ResultData data, final Response response) {
            retries.set(0);
            ...
        }

        @Override
        public void failure(RetrofitError error) {
            if (RetrofitErrorHandler.retry(activity, progressDialog, error, retries, activity.getString(R.string.error_getting_data))) {
                getDataFromServer(dataId);// retry this method
            } else {
               // let user know we counldn't get data
               ...
            }
        }
    });

}

Here you could check if the request should be retried and return true if we should retry and false if we shouldn't.

The first half of retry checks if the status code of the error (see r.getStatus()) equals which ever status code is of interest to you (see STATUS_CODE_RETRY) which could be any server status code (e.g. 401, 404. etc.).

The second half of retry first checks to see if it was a network error (see error.isNetworkError()) and if it is then it increments the retry counter and returns true which means that the network request will be retried. If the error is a network error (see error.isNetworkError()) and the retry counter is greater than your desired max number of retries (see NUM_RETRIES) then it returns false so that the request doesn't get triggered again. The next portion is the no connection check, where a network error has occurred but it's not a timeout so it must be an issue with the connection. This check returns false, as the network request should not be retried and the user should be notified of the connection issues. The final check is the non network error check, which indicates that an error has occurred that isn't a result of a network error. Once again, false is returned so the request isn't retried and the user is notified.

public static boolean retry(Activity act, RetrofitError error, AtomicInteger retries, String uploadErrorMsg){
    // this is the first half of the retry check
    Response r = error.getResponse();
    if (r != null && r.getStatus() == STATUS_CODE_RETRY){
        Log.v(TAG, "STATUS_CODE_RETRY!");
        ...
        return true;
    }
    // this is the second half of the retry check
    if (error.isNetworkError()) {
        if (error.getCause() instanceof SocketTimeoutException) {//connection timeout check
            if(retries.incrementAndGet() < NUM_RETRIES){ // retry if you can
                return true;
            } else { // if you can't retry anymore
                retries.set(0);
                Log.i(TAG, act.getClass().getSimpleName() + " has no more retries " + act.getString(R.string.timeout_msg));
                return false;
            }
        } else {//no connection check
            retries.set(0);
            Log.i(TAG, act.getClass().getSimpleName() + " " + act.getString(R.string.timeout_msg)+ " " + uploadErrorMsg);
            return false;
        }
    } else { //non network error check
        retries.set(0); 
        Log.i(TAG, act.getClass().getSimpleName() + " " + act.getString(R.string.err_msg)+ " " + uploadErrorMsg);
        return false;
    }
}  

Hope this helped.

like image 43
MrEngineer13 Avatar answered Oct 16 '22 04:10

MrEngineer13