Хороший подход к повторным запросам вручную в 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;
}
}
Надеюсь, что это помогло.