Search code examples
javaandroidokhttp

Android okHttp Retry policy


I am trying to create a simple wrapper which will call the server download the information and parse the binary data sent . for the connection I am using the library called okhttp , since the connection on 3G is not very reliable I have decided to implement a very simple re-try functionality using the following function**(Note this method will be always called from a background thread)**

private InputStream callServer() throws ServerException, NoNetworkAvailableException, ConnectionErrorException {
        NetworkOperation networkOperation = getNetworkOperation();
        InputStream inputStream = null;
        //in case of network problems we will retry 3 times separated by 5 seconds before gave up
        while (connectionFailedRetryCounter < connectionFailedMaximumAllowedRetries()) {
            connectionFailedRetryCounter++;
            try {
                inputStream = networkOperation.execute();
                break;//if this line was reached it means a successfull operation, no need to retry .
            } catch (ConnectionErrorException e) {
                if (canRetryToConnect()) {
                    Utils.forceSleepThread(Constants.Communications.ConnectionFailedTrialCounter.SLEEP_BETWEEN_REQUESTS_MILLI);//retry after 5 secs (Thread.sleep)
                } else {
                    throw e;//I give up
                }
            }

        }
        return inputStream;

    }

    private boolean canRetryToConnect() {
        return (connectionFailedRetryCounter < connectionFailedMaximumAllowedRetries()) && !canceled;
    }

Is this the right way to do this ? or is it already done by the library it self(there is no need to implement anything like this) ?

Here is what the method execute() do

public InputStream execute() throws ConnectionErrorException, NoNetworkAvailableException, ServerException {

    if (!Utils.isNetworkAvailable(context)) {
        throw new NoNetworkAvailableException();
    }

    Response response = doExecute();

    if (!response.isSuccessful()) {
        throw new ServerException(response.code());
    }

    return response.body().byteStream();
}

private Response doExecute() throws ConnectionErrorException {
    Response response;
    try {
        if (getRequestType() == RequestType.GET) {
            response = executeGet();
        } else {
            response = executePost();
        }
    } catch (IOException e) {
        throw new ConnectionErrorException();
    }
    return response;
}

Solution

  • You can avoid retrying if you catch NoNetworkAvailableException. Don't retry if you know following attempts will fail anyway.

    I would make connectionFailedMaximumAllowedRetries() a constant. I doubt you will need to change the variable at any point.

    Implement exponential back off. You could have it retry 10 times. Each time, you multiply the delay by 2 (with a cap of a few minutes). For example:

    1. Try call - failed
    2. Wait 1 second
    3. Try call - failed
    4. Wait 2 seconds
    5. Try call - failed
    6. Wait 4 seconds
    7. ...
    8. Try call - succeeded

    This is very typical behaviour. In the event of a short outage, the call will be made again very quickly and succeed. In the event of a longer outage, you don't want to be calling constantly every few seconds. This gives your code the best chance of having its call go through. Obviously, attention should be made to not annoy the user if this call is required for a UI change.