安卓okHttp重试策略

3
我正在尝试创建一个简单的包装器,它将调用服务器下载信息并解析发送的二进制数据。 对于连接,我使用名为okhttp的库,由于3G上的连接不是非常可靠,因此我决定使用以下函数实现非常简单的重试功能**(请注意,此方法始终将从后台线程调用)**
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;
    }

这是否是正确的做法?或者库本身已经实现了此功能(不需要再实现此类内容)?

以下是execute()方法所执行的操作:

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;
}
1个回答

2

如果捕获到NoNetworkAvailableException,则可以避免重试。如果你知道后续的尝试也会失败,就不要重试。

我会把connectionFailedMaximumAllowedRetries()设为常量。我怀疑你在任何时候都不需要更改这个变量。

实现指数退避。你可以让它重试10次。每次,将延迟乘以2(最长几分钟)。例如:

  1. 尝试调用-失败
  2. 等待1秒钟
  3. 尝试调用-失败
  4. 等待2秒钟
  5. 尝试调用-失败
  6. 等待4秒钟
  7. ...
  8. 尝试调用-成功

这是非常典型的行为。在短暂的故障事件中,调用将很快再次进行并成功。在较长的故障事件中,你不希望每隔几秒钟就不断地进行调用。这给了你的代码最好的机会来完成调用。显然,如果此调用需要用于UI更改,应注意不要惹恼用户。


如果出现ConnectionErrorException,我只会重新尝试,如果出现其他异常(例如:NoNetworkAvailableException),则循环将中断并将异常传播到下一级(尽管我认为我的代码仍然很复杂且不易读懂,但我会努力简化流程)。指数退避的想法很棒。 - A.Alqadomi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接