Как я должен обрабатывать "нет подключения к интернету" с Retrofit на Android


Я хотел бы обрабатывать ситуации, когда нет подключения к интернету. Обычно я бежал:

ConnectivityManager cm =
    (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
                  activeNetwork.isConnectedOrConnecting();

(от здесь) перед отправкой запросов в сеть и уведомить пользователя, если не было подключения к интернету.

из того, что я видел, Retrofit не обрабатывает эту ситуацию специально. Если нет подключения к интернету, я просто сделать RetrofitError с таймаутом в качестве причины.

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

спасибо

Алекс

7 86

7 ответов:

то, что я в конечном итоге сделал, это создание пользовательского клиента дооснащения, который проверяет подключение перед выполнением запроса и выдает исключение.

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

и затем использовать его при настройке RestAdapter

RestAdapter.Builder().setEndpoint(serverHost)
                     .setClient(new ConnectivityAwareUrlClient(new OkHttpClient(), ...))

С момента дооснащения 1.8.0 это был осужден

retrofitError.isNetworkError()

вы должны использовать

if (retrofitError.getKind() == RetrofitError.Kind.NETWORK)
{

}

существует несколько типов ошибок, которые вы можете обрабатывать:

NETWORK исключение IOException произошло во время связи с сервером, например, тайм-аут, отсутствие соединения и т. д...

CONVERSION исключение было вызвано при (de)сериализации тела.

HTTP от сервера был получен код состояния HTTP не 200, например 502, 503, так далее...

UNEXPECTED внутренняя ошибка при попытке выполнить запрос. Рекомендуется повторно запустить это исключение, чтобы ваше приложение аварийно завершило работу.

@AlexV вы уверены, что RetrofitError содержит тайм-аут в качестве причины (SocketTimeOutException при вызове getCause ()), когда нет подключения к интернету?

насколько я знаю, когда нет подключения к интернету RetrofitError содержит ConnectionException в качестве причины.

Если вы реализуете ErrorHandler вы можете сделать что-то вроде этого:

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

}

С Retrofit 2 мы используем реализацию перехватчика OkHttp для проверки сетевого подключения перед отправкой запроса. Если нет сети, создайте исключение по мере необходимости.

Это позволяет специально обрабатывать проблемы с подключением к сети перед ударом модернизации.

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Response;
import rx.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";
    }
}

когда вы Throwable ошибка из вашего http-запроса вы можете определить, является ли это сетевой ошибкой с помощью такого метода:

String getErrorMessage(Throwable e) {
    RetrofitError retrofitError;
    if (e instanceof RetrofitError) {
        retrofitError = ((RetrofitError) e);
        if (retrofitError.getKind() == RetrofitError.Kind.NETWORK) {
            return "Network is down!";
        }
    }
}

обновление: Я должен сказать, что это работает только с Retrofit 1, а не с Retrofit 2.

вы можете использовать этот код

ответ.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;
    }
}

реализация в методах

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

просто сделайте это, вы будете уведомлены даже для таких вопросов, как

UnknownHostException

,

SocketTimeoutException

и другие.

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