Je voudrais gérer les situations où il n'y a pas de connexion Internet. D'habitude je courrais:
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
(à partir de ici ) avant d'envoyer les demandes au réseau et d'avertir l'utilisateur s'il n'y a pas de connexion Internet.
D'après ce que j'ai vu, Retrofit ne gère pas cette situation spécifiquement. S'il n'y a pas de connexion Internet, j'obtiendrai simplement RetrofitError
avec timeout.
Si je souhaite intégrer ce type de contrôle à chaque demande HTTP avec Retrofit, comment dois-je le faire? Ou devrais-je le faire du tout.
Merci
Alex
J'ai fini par créer un client de modernisation personnalisé qui vérifie la connectivité avant d'exécuter une demande et lève une exception.
public class ConnectivityAwareUrlClient implements Client {
Logger log = LoggerFactory.getLogger(ConnectivityAwareUrlClient.class);
public ConnectivityAwareUrlClient(Client wrappedClient, NetworkConnectivityManager ncm) {
this.wrappedClient = wrappedClient;
this.ncm = ncm;
}
Client wrappedClient;
private NetworkConnectivityManager ncm;
@Override
public Response execute(Request request) throws IOException {
if (!ncm.isConnected()) {
log.debug("No connectivity %s ", request);
throw new NoConnectivityException("No connectivity");
}
return wrappedClient.execute(request);
}
}
puis utilisez-le lors de la configuration de RestAdapter
RestAdapter.Builder().setEndpoint(serverHost)
.setClient(new ConnectivityAwareUrlClient(new OkHttpClient(), ...))
Depuis la rénovation 1.8.0
ceci est déconseillé
retrofitError.isNetworkError()
vous devez utiliser
if (retrofitError.getKind() == RetrofitError.Kind.NETWORK)
{
}
vous pouvez gérer plusieurs types d’erreurs:
NETWORK
Une exception IOException s'est produite lors de la communication avec le serveur, par exemple. Timeout, Pas de connexion, etc ...
CONVERSION
Une exception a été levée lors de la (dé) sérialisation d'un corps.
HTTP
Un code d'état HTTP autre que 200 a été reçu du serveur, par exemple. 502, 503, etc ...
UNEXPECTED
Une erreur interne s'est produite lors de la tentative d'exécution d'une demande. Il est recommandé de relancer cette exception pour que votre application se bloque.
@AlexV êtes-vous sûr que RetrofitError contient un délai d'expiration en tant que raison (SocketTimeOutException lorsque getCause () est appelée) lorsqu'il n'existe pas de connexion Internet?
Pour autant que je sache en l'absence de connexion Internet, RetrofitError contient une exception ConnectionException comme cause.
Si vous implémentez un ErrorHandler , vous pouvez faire quelque chose comme ceci:
public class RetrofitErrorHandler implements ErrorHandler {
@Override
public Throwable handleError(RetrofitError cause) {
if (cause.isNetworkError()) {
if (cause.getCause() instanceof SocketTimeoutException) {
return new MyConnectionTimeoutException();
} else {
return new MyNoConnectionException();
}
} else {
[... do whatever you want if it's not a network error ...]
}
}
}
Avec Retrofit 2, nous utilisons une implémentation OkHttp Interceptor pour vérifier la connectivité du réseau avant l’envoi de la demande. Si aucun réseau, lève une exception selon le cas.
Cela permet de gérer spécifiquement les problèmes de connectivité réseau avant de lancer Retrofit.
import Java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Response;
import io.reactivex.Observable
public class ConnectivityInterceptor implements Interceptor {
private boolean isNetworkActive;
public ConnectivityInterceptor(Observable<Boolean> isNetworkActive) {
isNetworkActive.subscribe(
_isNetworkActive -> this.isNetworkActive = _isNetworkActive,
_error -> Log.e("NetworkActive error " + _error.getMessage()));
}
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
if (!isNetworkActive) {
throw new NoConnectivityException();
}
else {
Response response = chain.proceed(chain.request());
return response;
}
}
}
public class NoConnectivityException extends IOException {
@Override
public String getMessage() {
return "No network available, please check your WiFi or Data connection";
}
}
Lorsque vous obtenez une erreur Throwable
de votre demande http, vous pouvez détecter s'il s'agit d'une erreur réseau avec une méthode comme celle-ci:
String getErrorMessage(Throwable e) {
RetrofitError retrofitError;
if (e instanceof RetrofitError) {
retrofitError = ((RetrofitError) e);
if (retrofitError.getKind() == RetrofitError.Kind.NETWORK) {
return "Network is down!";
}
}
}
UPDATE : Je dois dire que cela ne fonctionne qu'avec Retrofit 1, pas Retrofit 2.
faites cela, vous serez averti même pour des problèmes comme
UnknownHostException
,
SocketTimeoutException
et d'autres.
@Override public void onFailure(Call<List<BrokenGitHubRepo>> call, Throwable t) {
if (t instanceof IOException) {
Toast.makeText(ErrorHandlingActivity.this, "this is an actual network failure :( inform the user and possibly retry", Toast.LENGTH_SHORT).show();
// logging probably not necessary
}
else {
Toast.makeText(ErrorHandlingActivity.this, "conversion issue! big problems :(", Toast.LENGTH_SHORT).show();
// todo log to some central bug tracking service
} }
vous pouvez utiliser ce code
Response.Java
import com.google.gson.annotations.SerializedName;
/**
* Created by hackro on 19/01/17.
*/
public class Response {
@SerializedName("status")
public String status;
public void setStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
@SuppressWarnings({"unused", "used by Retrofit"})
public Response() {
}
public Response(String status) {
this.status = status;
}
}
NetworkError.Java
import Android.text.TextUtils;
import com.google.gson.Gson;
import Java.io.IOException;
import Java.util.List;
import Java.util.Map;
import retrofit2.adapter.rxjava.HttpException;
import static Java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
/**
* Created by hackro on 19/01/17.
*/
public class NetworkError extends Throwable {
public static final String DEFAULT_ERROR_MESSAGE = "Please try again.";
public static final String NETWORK_ERROR_MESSAGE = "No Internet Connection!";
private static final String ERROR_MESSAGE_HEADER = "Error Message";
private final Throwable error;
public NetworkError(Throwable e) {
super(e);
this.error = e;
}
public String getMessage() {
return error.getMessage();
}
public boolean isAuthFailure() {
return error instanceof HttpException &&
((HttpException) error).code() == HTTP_UNAUTHORIZED;
}
public boolean isResponseNull() {
return error instanceof HttpException && ((HttpException) error).response() == null;
}
public String getAppErrorMessage() {
if (this.error instanceof IOException) return NETWORK_ERROR_MESSAGE;
if (!(this.error instanceof HttpException)) return DEFAULT_ERROR_MESSAGE;
retrofit2.Response<?> response = ((HttpException) this.error).response();
if (response != null) {
String status = getJsonStringFromResponse(response);
if (!TextUtils.isEmpty(status)) return status;
Map<String, List<String>> headers = response.headers().toMultimap();
if (headers.containsKey(ERROR_MESSAGE_HEADER))
return headers.get(ERROR_MESSAGE_HEADER).get(0);
}
return DEFAULT_ERROR_MESSAGE;
}
protected String getJsonStringFromResponse(final retrofit2.Response<?> response) {
try {
String jsonString = response.errorBody().string();
Response errorResponse = new Gson().fromJson(jsonString, Response.class);
return errorResponse.status;
} catch (Exception e) {
return null;
}
}
public Throwable getError() {
return error;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NetworkError that = (NetworkError) o;
return error != null ? error.equals(that.error) : that.error == null;
}
@Override
public int hashCode() {
return error != null ? error.hashCode() : 0;
}
}
Implémentation dans vos méthodes
@Override
public void onCompleted() {
super.onCompleted();
}
@Override
public void onError(Throwable e) {
super.onError(e);
networkError.setError(e);
Log.e("Error:",networkError.getAppErrorMessage());
}
@Override
public void onNext(Object obj) { super.onNext(obj);
}