Android에서 Retrofit으로 “인터넷 연결 없음”을 어떻게 처리해야합니까?

인터넷에 연결되지 않은 상황을 처리하고 싶습니다. 일반적으로 다음을 실행합니다.

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

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

( 여기에서 ) 네트워크에 요청을 보내기 전에 인터넷에 연결되지 않은 경우 사용자에게 알립니다.

내가 본 것에서 Retrofit은이 상황을 구체적으로 처리하지 않습니다. 인터넷에 연결되어 있지 않으면 RetrofitError시간 초과를 이유로 하겠습니다 .

Retrofit을 사용하여 모든 HTTP 요청에 이러한 종류의 검사를 통합하려면 어떻게해야합니까? 아니면 내가해야만합니다.

감사

알렉스



답변

내가 한 일은 요청을 실행하기 전에 연결을 확인하고 예외를 던지는 사용자 지정 Retrofit 클라이언트를 만드는 것입니다.

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 본문을 (역) 직렬화하는 동안 예외가 발생했습니다.

HTTP 200이 아닌 HTTP 상태 코드가 서버에서 수신되었습니다 (예 : 502, 503 등).

UNEXPECTED요청을 실행하는 동안 내부 오류가 발생했습니다. 애플리케이션이 충돌하도록이 예외를 다시 발생시키는 것이 가장 좋습니다.


답변

Retrofit 2에서는 OkHttp 인터셉터 구현을 사용하여 요청을 보내기 전에 네트워크 연결을 확인합니다. 네트워크가 없으면 적절하게 예외를 발생시킵니다.

이를 통해 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";
    }
}


답변

@AlexV 인터넷에 연결되어 있지 않을 때 RetrofitError가 이유 (getCause ()가 호출 될 때 SocketTimeOutException)로 시간 초과를 포함한다고 확신합니까?

인터넷 연결이 없을 때 내가 아는 한 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 ...]
        }
    }

}


답변

개 조용 1

당신이 얻을 때 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!";
        }
    }
}


답변

이렇게하면 다음과 같은 문제에 대해서도 알림을 받게됩니다.

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


답변

이 코드를 사용할 수 있습니다

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

방법의 구현

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