Хороший подход к повторным запросам вручную в Retrofit Android

Я понимаю, что переоснащение автоматически повторяется при сбоях, но я хочу проверить конкретную ошибку. Если ошибка связана с определенным HTTP-кодом, мне нужно повторить этот запрос после его изменения.

Это можно легко сделать с помощью синхронных вызовов, но я делаю асинхронный вызов (по передача обратных вызовов). Когда я получаю ошибку в обратном вызове, я хочу повторить запрос, но все, что у меня есть, это RetrofitError (и я потерял тело запроса).

У меня есть несколько запросов, идущих от одного и того же действия (одновременно), и поэтому я избегаю сохранения всех запросов и их сведение к успеху.

Есть ли лучший способ достичь этого требования?

Ответ 1

Если вы используете OkHttp в качестве HttpClient и обновлены до Retrofit >= 1.9.0, тогда вы можете используйте новый Interceptor. В частности, Application Interceptor позволит вам retry and make multiple calls.

Вы можете увидеть пример псевдокода. Я отправил по аналогичному вопросу для обработки истекших токенов.

Также обратите внимание: Retrofit 2.0 будет иметь Interceptors в соответствии с этот билет GitHub. Это устранит зависимость от OkHttp, хотя я по-прежнему рекомендую ее использовать.

Ответ 2

Вот что вы могли бы попробовать. Сначала метод запроса сети:

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
               ...
            }
        }
    });

}

Здесь вы можете проверить, нужно ли повторить попытку запроса и вернуть true, если мы должны повторить попытку и false, если мы этого не сделаем.

Первая половина проверки проверяет, действительно ли код состояния ошибки (см. r.getStatus()), который всегда интересует вас код состояния (см. STATUS_CODE_RETRY), который может быть любым кодом состояния сервера (например, 401, 404 и т.д.).

Вторая половина попытки сначала проверяет, была ли она сетевой ошибкой (см. error.isNetworkError()), а затем она увеличивает счетчик повторов и возвращает значение true, что означает, что запрос сети будет повторен. Если ошибка является сетевой ошибкой (см. error.isNetworkError()), а счетчик повторов больше требуемого максимального количества попыток (см. NUM_RETRIES), тогда он возвращает false, чтобы запрос не запускался снова. Следующая часть - это не проверка соединения, где произошла сетевая ошибка, но это не тайм-аут, поэтому это должно быть проблемой с соединением. Эта проверка возвращает false, поскольку сетевой запрос не следует повторять, и пользователь должен получать уведомления о проблемах с подключением. Окончательная проверка - это проверка не сетевой ошибки, которая указывает, что произошла ошибка, которая не является результатом сетевой ошибки. И снова возвращается false, поэтому запрос не повторяется и пользователь уведомляется.

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;
    }
}  

Надеюсь, что это помогло.