library implementar example ejemplo body android retrofit okhttp

android - implementar - retrofit2 library



¿Cómo volver a intentar las solicitudes HTTP con OkHttp/Retrofit? (10)

Estoy usando Retrofit / OkHttp (1.6) en mi proyecto de Android.

No encuentro ningún mecanismo de reintento de solicitud incorporado en ninguno de ellos. Al buscar más, leo OkHttp parece tener silenciosos reintentos. No veo que eso ocurra en ninguna de mis conexiones (HTTP o HTTPS). ¿Cómo configurar reintentos con okclient?

Por ahora, estoy atrapando excepciones y reintentando el mantenimiento de una variable contraria.


Como se indica en los docs , una mejor opción sería utilizar los autenticadores horneados, por ejemplo: cliente OkHttpClient final privado = nuevo OkHttpClient ();

public void run() throws Exception { client.setAuthenticator(new Authenticator() { @Override public Request authenticate(Proxy proxy, Response response) { System.out.println("Authenticating for response: " + response); System.out.println("Challenges: " + response.challenges()); String credential = Credentials.basic("jesse", "password1"); return response.request().newBuilder() .header("Authorization", credential) .build(); } @Override public Request authenticateProxy(Proxy proxy, Response response) { return null; // Null indicates no attempt to authenticate. } }); Request request = new Request.Builder() .url("http://publicobject.com/secrets/hellosecret.txt") .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); }


Cortesía de la respuesta principal: Esto es lo que funcionó para mí. Si hay problemas de conectividad, es mejor esperar unos segundos antes de volver a intentarlo.

public class ErrorInterceptor implements Interceptor { ICacheManager cacheManager; Response response = null; int tryCount = 0; int maxLimit = 3; int waitThreshold = 5000; @Inject public ErrorInterceptor() { } @Override public Response intercept(Chain chain){ // String language = cacheManager.readPreference(PreferenceKeys.LANGUAGE_CODE); Request request = chain.request(); response = sendReqeust(chain,request); while (response ==null && tryCount < maxLimit) { Log.d("intercept", "Request failed - " + tryCount); tryCount++; try { Thread.sleep(waitThreshold); // force wait the network thread for 5 seconds } catch (InterruptedException e) { e.printStackTrace(); } response = sendReqeust(chain,request); } return response; } private Response sendReqeust(Chain chain, Request request){ try { response = chain.proceed(request); if(!response.isSuccessful()) return null; else return response; } catch (IOException e) { return null; } }

}


El problema con response.isSuccessful () es cuando tienes una excepción como SocketTimeoutException.

Modifiqué el código original para arreglarlo.

OkHttpClient client = new OkHttpClient(); client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.interceptors().add(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = null; boolean responseOK = false; int tryCount = 0; while (!responseOK && tryCount < 3) { try { response = chain.proceed(request); responseOK = response.isSuccessful(); }catch (Exception e){ Log.d("intercept", "Request is not successful - " + tryCount); }finally{ tryCount++; } } // otherwise just pass the original response on return response; } });

Espero eso ayude. Saludos.


Encontré que el camino (interceptor OKHttpClient) proporcionado por Sinan Kozak no funciona cuando falló la conexión http, todavía no hay nada relacionado con la respuesta HTTP.

Así que utilizo otra forma de enganchar el objeto Observable, invoco .retrycuando lo hago. Además, he agregado el límite retryCount.

import retrofit2.Call; import retrofit2.CallAdapter; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.HttpException; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.jackson.JacksonConverterFactory; import rx.Observable; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type;

Entonces

RxJavaCallAdapterFactory originCallAdaptorFactory = RxJavaCallAdapterFactory.create(); CallAdapter.Factory newCallAdaptorFactory = new CallAdapter.Factory() { @Override public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { CallAdapter<?> ca = originCallAdaptorFactory.get(returnType, annotations, retrofit); return new CallAdapter<Observable<?>>() { @Override public Type responseType() { return ca.responseType(); } int restRetryCount = 3; @Override public <R> Observable<?> adapt(Call<R> call) { Observable<?> rx = (Observable<?>) ca.adapt(call); return rx.retryWhen(errors -> errors.flatMap(error -> { boolean needRetry = false; if (restRetryCount >= 1) { if (error instanceof IOException) { needRetry = true; } else if (error instanceof HttpException) { if (((HttpException) error).code() != 200) { needRetry = true; } } } if (needRetry) { restRetryCount--; return Observable.just(null); } else { return Observable.error(error); } })); } }; } };

Luego agrega o reemplaza

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

con

.addCallAdapterFactory(newCallAdaptorFactory)

Por ejemplo:

return new Retrofit .Builder() .baseUrl(baseUrl) .client(okClient) .addCallAdapterFactory(newCallAdaptorFactory) .addConverterFactory(JacksonConverterFactory.create(objectMapper));

Nota: Para simplificar, simplemente trato el código HTTP> código 404 como volver a intentar, por favor modifíquelo usted mismo.

Además, si la respuesta http es 200, entonces rx.retryWhen anterior no se llamará, si insiste en comprobar tal respuesta, puede agregar rx.subscribeOn(...throw error... before .retryWhen.


He jugado mucho con este problema tratando de encontrar cómo es la mejor manera de volver a intentar las solicitudes de Retrofit. Estoy usando Retrofit 2 así que mi solución es para Retrofit 2. Para Retrofit 1 debes usar Interceptor como la respuesta aceptada aquí. La respuesta de @joluet es correcta, pero no mencionó que el método de reintento debe invocarse antes del método .subscribe (onComplete, onError). Esto es muy importante; de ​​lo contrario, la solicitud no se volvería a intentar como @pocmo mencionada en @joluet answer. Aquí está mi ejemplo:

final Observable<List<NewsDatum>> newsDetailsObservable = apiService.getCandidateNewsItem(newsId).map((newsDetailsParseObject) -> { return newsDetailsParseObject; }); newsDetailsObservable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry((integer, throwable) -> { //MAX_NUMBER_TRY is your maximum try number if(integer <= MAX_NUMBER_TRY){ return true;//this will retry the observable (request) } return false;//this will not retry and it will go inside onError method }) .subscribe(new Subscriber<List<NewsDatum>>() { @Override public void onCompleted() { // do nothing } @Override public void onError(Throwable e) { //do something with the error } @Override public void onNext(List<NewsDatum> apiNewsDatum) { //do something with the parsed data } });

apiService es mi objeto RetrofitServiceProvider.

Por cierto: estoy usando Java 8 por lo que muchas expresiones lambda están dentro del código.


No sé si esta es una opción para ti, pero podrías usar RxJava junto con Retrofit.

Retrofit puede devolver Observables en llamadas de descanso. En Oberservables puedes simplemente llamar a retry(count) para volver a suscribirte al Observable cuando emita un error.

Debería definir la llamada en su interfaz de esta manera:

@GET("/data.json") Observable<DataResponse> fetchSomeData();

Entonces puedes suscribirte a este Observable así:

restApi.fetchSomeData() .retry(5) // Retry the call 5 times if it errors .subscribeOn(Schedulers.io()) // execute the call asynchronously .observeOn(AndroidSchedulers.mainThread()) // handle the results in the ui thread .subscribe(onComplete, onError); // onComplete and onError are of type Action1<DataResponse>, Action1<Throwable> // Here you can define what to do with the results

Tuve el mismo problema que tú y esta fue realmente mi solución. RxJava es una biblioteca muy buena para usar en combinación con Retrofit. Incluso puede hacer muchas cosas interesantes además de volver a intentar (como, por ejemplo, componer y encadenar llamadas ).


Para aquellos que prefieren un interceptor para tratar el tema de reintentar: basándose en la respuesta de Sinan, este es mi interceptor propuesto, que incluye el recuento de reintentos y el retardo de rellamada, y solo reintenta los intentos cuando la red está disponible, y cuando la solicitud no fue cancelado. (solo se refiere a IOExceptions (SocketTimeout, UnknownHost, etc.))

builder.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // try the request Response response = null; int tryCount = 1; while (tryCount <= MAX_TRY_COUNT) { try { response = chain.proceed(request); break; } catch (Exception e) { if (!NetworkUtils.isNetworkAvailable()) { // if no internet, dont bother retrying request throw e; } if ("Canceled".equalsIgnoreCase(e.getMessage())) { // Request canceled, do not retry throw e; } if (tryCount >= MAX_TRY_COUNT) { // max retry count reached, giving up throw e; } try { // sleep delay * try count (e.g. 1st retry after 3000ms, 2nd after 6000ms, etc.) Thread.sleep(RETRY_BACKOFF_DELAY * tryCount); } catch (InterruptedException e1) { throw new RuntimeException(e1); } tryCount++; } } // otherwise just pass the original response on return response; } });



Solo quiero compartir mi versión. Utiliza el método rxJava retryWhen. Mi versión vuelve a intentar la conexión cada N = 15 segundos y casi de inmediato emite el reintento cuando se recupera la conexión a Internet.

public class RetryWithDelayOrInternet implements Function<Flowable<? extends Throwable>, Flowable<?>> { public static boolean isInternetUp; private int retryCount; @Override public Flowable<?> apply(final Flowable<? extends Throwable> attempts) { return Flowable.fromPublisher(s -> { while (true) { retryCount++; try { Thread.sleep(1000); } catch (InterruptedException e) { attempts.subscribe(s); break; } if (isInternetUp || retryCount == 15) { retryCount = 0; s.onNext(new Object()); } } }) .subscribeOn(Schedulers.single()); }}

Y deberías usarlo antes. Suscríbete así:

.retryWhen(new RetryWithDelayOrInternet())

Debería cambiar manualmente el campo isInternetUp

public class InternetConnectionReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { boolean networkAvailable = isNetworkAvailable(context); RetryWithDelayOrInternet.isInternetUp = networkAvailable; } public static boolean isNetworkAvailable(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); }}


Para Retrofit 1.x;

Puedes usar Interceptors . Crea un interceptor personalizado

OkHttpClient client = new OkHttpClient(); client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.interceptors().add(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // try the request Response response = chain.proceed(request); int tryCount = 0; while (!response.isSuccessful() && tryCount < 3) { Log.d("intercept", "Request is not successful - " + tryCount); tryCount++; // retry the request response = chain.proceed(request); } // otherwise just pass the original response on return response; } });

Y úsala mientras creas RestAdapter.

new RestAdapter.Builder() .setEndpoint(API_URL) .setRequestInterceptor(requestInterceptor) .setClient(new OkClient(client)) .build() .create(Adapter.class);

Para Retrofit 2.x;

Puede usar el método Call.clone() para clonar solicitud y ejecutarla.