Android'de Retrofit ile "İnternet bağlantısı yok" ile nasıl başa çıkmalıyım?


119

İnternet bağlantısının olmadığı durumlarla ilgilenmek istiyorum. Genellikle koşardım:

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

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

( buradan ) istekleri ağa göndermeden önce ve internet bağlantısı yoksa kullanıcıyı bilgilendirin.

Gördüğüm kadarıyla Retrofit bu durumu özel olarak ele almıyor. İnternet bağlantısı yoksa RetrofitErrorbir sebep olarak zaman aşımına uğrayacağım.

Bu tür bir denetimi Retrofit ile her HTTP isteğine dahil etmek istersem, bunu nasıl yapmalıyım? Yoksa hiç yapmalı mıyım?

Teşekkürler

Alex


Http bağlantısı için zaman aşımı istisnasını yakalamak için try-catch bloğunu kullanabilirsiniz. Ardından, kullanıcılara internet bağlantı durumu hakkında bilgi verin. Hoş değil ama alternatif bir çözüm.
Tugrul

8
Evet, ancak soketten bağlantı zaman aşımına
uğramayı

Android Query, "internet yok" durumunu işler ve kısa süre içinde ilgili hata kodunu döndürür. Belki onu birQuery ile değiştirmeye değer? Diğer bir çözüm, ağ değişikliklerinde bir dinleyici oluşturmaktır ve böylece uygulama, isteği göndermeden önce internet kullanılabilirliğini bilecektir.
Stan

güçlendirme 2 için bkz. github.com/square/retrofit/issues/1260
ghanbari

Yanıtlar:


63

Sonunda yaptığım şey, bir isteği yerine getirmeden önce bağlantıyı kontrol eden ve bir istisna atan özel bir Retrofit istemcisi oluşturmaktı.

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

ve sonra yapılandırırken kullanın RestAdapter

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

1
NetworkConnectivityManager sınıfınız nereden geliyor? Özel?
NPike

Evet, özel. Temelde sorudaki kod
AlexV

2
OKClient () kullanmak yerine OkHttpClient çalışmıyor BDW güzel cevap. teşekkür ederim.
Harshvardhan Trivedi

Teşekkürler :-). Yanıtınızı OkHttp'nin doğru kullanımıyla düzenlediniz.
user1007522

1
@MominAlAziz nasıl tanımladığına bağlı NoConnectivityException, ya da uzatmak yapabilir IOExceptionya da uzatmak yapmakRuntimeException
AlexV

45

Güçlendirmeden 1.8.0bu yana bu kullanımdan kaldırıldı

retrofitError.isNetworkError()

kullanmak zorundasın

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

}

işleyebileceğiniz birden çok hata türü vardır:

NETWORK Sunucuyla iletişim kurulurken bir IOException oluştu, örneğin Zaman Aşımı, Bağlantı yok, vb.

CONVERSION Bir gövde (de) serileştirilirken bir istisna atıldı.

HTTP Sunucudan 200 olmayan bir HTTP durum kodu alındı, örn. 502, 503, vb ...

UNEXPECTEDBir isteği yürütmeye çalışırken dahili bir hata oluştu. Uygulamanızın çökmesi için bu istisnayı yeniden atmak en iyi uygulamadır.


8
Kullanmaktan kaçının equalskullanmak her zaman, değişkenler üzerinde CONSTANT.equals(variable)olası NullPointerException önlemek için yerine. Hatta daha da iyisi, bu durumda, çeteleler == karşılaştırma böylece kabul error.getKind() == RetrofitError.Kind.NETWORKdaha iyi bir yaklaşım olabilir
MariusBudin

1
NPE'lerden ve diğer sözdizimi sınırlamalarından / sıkıntılarından sıkıldıysanız Java'yı Kotlin ile değiştirin
Louis CAD

42

Retrofit 2 ile, isteği göndermeden önce ağ bağlantısını kontrol etmek için bir OkHttp Interceptor uygulaması kullanıyoruz. Ağ yoksa, uygun şekilde bir istisna atın.

Bu, bir kişinin Retrofit'e basmadan önce ağ bağlantısı sorunlarını özel olarak ele almasını sağlar.

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

3
Uçak modunu açıp kapatırsanız kusurludur, çünkü yalnızca gözlemlenebilir olanı işe yaramaz hale getirdikten sonra değişkeni ayarlıyorsunuz.
Oliver Dixon

3
Yani OkHttpClient.Builder.addInterceptor(new ConnectivityInterceptor(HERE))BURADA ne olmalı?
Rumid

3
İnsanların resmin tamamını görmesi için lütfen bu kodu ekleyebilir misiniz?
Oliver Dixon

1
@Kevin, bağlantı kurulduğunda güncelleneceğinden nasıl emin olabilirim?
Rumid

3
bu kabul edilen cevap olmalıdır. burada daha fazla örnek: migapro.com/detect-offline-error-in-retrofit-2
j2emanue

35

@AlexV RetrofitError'ın internet bağlantısı olmadığında neden olarak bir zaman aşımı içerdiğinden emin misiniz (getCause () çağrıldığında SocketTimeOutException)?

İnternet bağlantısı olmadığında bildiğim kadarıyla RetrofitError neden olarak bir ConnectionException içeriyor.

Bir ErrorHandler uygularsanız, aşağıdaki gibi bir şey yapabilirsiniz:

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
Retrofit kaynağında kullanabileceğiniz bir ErrorHandler bulunmaktadır. Hatayı kendiniz çözmezseniz, Retrofit size [java.net.UnknownHostException: ana bilgisayar "example.com" çözümlenemiyor: Ana bilgisayar
adıyla

1
@Codeversed, böylece isNetworkErrorana bilgisayar hatasını çözemekten kurtulmak mı?
Omnipresent

2
bunu arayüzünüze nasıl uygularsınız müşteri? Demek istediğim, bu sınıfı neye bağlıyorsunuz?
CUM

23
neden.isNetworkError () kullanımdan kaldırıldı : useerror.getKind() == RetrofitError.Kind.NETWORK
Hugo Gresse

6

Güçlendirme için 1

ThrowableHttp talebinizden bir hata aldığınızda, bunun bir ağ hatası olup olmadığını aşağıdaki gibi bir yöntemle tespit edebilirsiniz:

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

5

sadece bunu yap, gibi sorunlar için bile haber vereceksin

UnknownHostException

,

SocketTimeoutException

ve diğerleri.

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

2

bu kodu kullanabilirsin

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

Yöntemlerinize uygulama

        @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);        
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.