web-dev-qa-db-fra.com

Comment gérer "Pas de connexion Internet" avec Retrofit on Android

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

106
AlexV

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(), ...))
59
AlexV

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.

43
Muhammad Alfaifi

@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 ...]  
        }
    }

}
34
saguinav

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";
    }
}
31
Kevin

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.

5
IgorGanapolsky

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
} }
5
Deepak sharma

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);        
    }
1
David Hackro