Làm cách nào để xử lý “Không có kết nối internet” với Trang bị thêm trên Android


119

Tôi muốn xử lý các tình huống khi không có kết nối internet. Thường thì tôi sẽ chạy:

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

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

(từ đây ) trước khi gửi các yêu cầu đến mạng và thông báo cho người dùng nếu không có kết nối internet.

Từ những gì tôi thấy Retrofit không xử lý tình huống này một cách cụ thể. Nếu không có kết nối internet, tôi sẽ lấy RetrofitErrorlý do là hết thời gian.

Nếu tôi muốn kết hợp loại kiểm tra này vào mọi yêu cầu HTTP với Retrofit, tôi nên làm như thế nào? Hay tôi nên làm điều đó ở tất cả.

Cảm ơn

Alex


Bạn có thể sử dụng khối try-catch để bắt ngoại lệ thời gian chờ cho kết nối http. Sau đó, thông báo cho người dùng về trạng thái kết nối internet. Không đẹp nhưng là một giải pháp thay thế.
Tugrul

8
Có, nhưng nó nhanh hơn nhiều để kiểm tra với Android nếu nó có kết nối internet thay vì chờ đợi để nhận thông báo timeout kết nối từ ổ cắm
AlexV

Android Query xử lý "không có internet" và trả về mã lỗi tương ứng đủ sớm. Có lẽ nó đáng để thay thế nó bằng aQuery? Một giải pháp khác là tạo trình lắng nghe về các thay đổi của mạng và do đó ứng dụng sẽ biết về tính khả dụng của internet trước khi gửi yêu cầu.
Stan

đối với trang bị thêm 2, hãy xem github.com/square/retrofit/issues/1260
ghanbari

Câu trả lời:


63

Những gì tôi đã kết thúc là tạo một ứng dụng khách Retrofit tùy chỉnh để kiểm tra kết nối trước khi thực hiện một yêu cầu và ném một ngoại lệ.

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

và sau đó sử dụng nó khi định cấu hình RestAdapter

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

1
Lớp NetworkConnectivityManager của bạn từ đâu? Tập quán?
NPike

Có, tùy chỉnh. Về cơ bản nó là mã từ câu hỏi
AlexV

2
OkHttpClient không hoạt động thay vì sử dụng OKClient (). Câu trả lời rất hay. cảm ơn bạn.
Harshvardhan Trivedi

Cảm ơn :-). Đã chỉnh sửa câu trả lời của bạn với việc sử dụng đúng OkHttp.
user1007522

1
@MominAlAziz tùy thuộc vào cách bạn định nghĩa NoConnectivityException, bạn có thể làm cho nó mở rộng IOExceptionhoặc làm cho nó mở rộngRuntimeException
AlexV

45

Kể từ khi trang bị thêm, 1.8.0điều này đã không được dùng nữa

retrofitError.isNetworkError()

bạn phải sử dụng

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

}

có nhiều loại lỗi bạn có thể xử lý:

NETWORK IOException xảy ra khi giao tiếp với máy chủ, ví dụ: Hết thời gian chờ, Không có kết nối, v.v.

CONVERSION Một ngoại lệ được đưa ra trong khi (de) tuần tự hóa một phần thân.

HTTP Mã trạng thái HTTP không phải 200 đã được nhận từ máy chủ, ví dụ: 502, 503, v.v.

UNEXPECTEDĐã xảy ra lỗi nội bộ khi cố gắng thực hiện một yêu cầu. Cách tốt nhất là ném lại ngoại lệ này để ứng dụng của bạn bị treo.


8
Tránh sử dụng equalsquá nhiều biến, luôn sử dụng CONSTANT.equals(variable)thay thế để tránh NullPointerException có thể xảy ra. Hoặc thậm chí tốt hơn trong trường hợp này, sự đếm chấp nhận == so sánh như vậy error.getKind() == RetrofitError.Kind.NETWORKcó thể là một cách tiếp cận tốt hơn
MariusBudin

1
Thay Java với Kotlin nếu bạn đang mệt mỏi của NPEs và hạn chế cú pháp khác / đau
Louis CAD

42

Với Retrofit 2, chúng tôi sử dụng triển khai OkHttp Interceptor để kiểm tra kết nối mạng trước khi gửi yêu cầu. Nếu không có mạng, hãy ném một ngoại lệ nếu thích hợp.

Điều này cho phép một người xử lý cụ thể các vấn đề kết nối mạng trước khi nhấn 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";
    }
}

3
Thật thiếu sót nếu bạn bật Chế độ trên máy bay sau đó tắt nó đi vì bạn chỉ đặt var một lần khiến việc quan sát trở nên vô dụng.
Oliver Dixon

3
Vì vậy, bạn đang thực hiện một OkHttpClient.Builder.addInterceptor(new ConnectivityInterceptor(HERE))Cái gì nên có ở ĐÂY?
Tin đồn

3
Làm ơn bạn có thể bao gồm mã đó để mọi người có được một bức tranh toàn cảnh không?
Oliver Dixon

1
@Kevin làm cách nào để đảm bảo rằng nó sẽ cập nhật sau khi có kết nối?
Tin đồn

3
đây phải là câu trả lời được chấp nhận. nhiều ví dụ về nó ở đây: migapro.com/detect-offline-error-in-retrofit-2
j2emanue

35

@AlexV bạn có chắc chắn rằng RetrofitError chứa thời gian chờ như một lý do (SocketTimeOutException khi getCause () được gọi) khi không có kết nối internet không?

Theo như tôi biết khi không có kết nối internet, nguyên nhân là do RetrofitError có chứa ConnectionException.

Nếu bạn triển khai một ErrorHandler, bạn có thể làm như sau:

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
Có một ErrorHandler được cung cấp trong nguồn Trang bị thêm mà bạn có thể sử dụng. Nếu bạn không tự xử lý lỗi, Retrofit sẽ cung cấp cho bạn lỗi Trang bị thêm của [java.net.UnknownHostException: Không thể giải quyết máy chủ lưu trữ "example.com": Không có địa chỉ liên kết với tên máy chủ]
Được mã hóa

1
@Codeversed vậy có isNetworkErrorthoát khỏi lỗi không thể giải quyết máy chủ không?
Toàn bộ

2
bạn sẽ triển khai điều này vào giao diện của mình như thế nào, khách hàng? Ý tôi là, bạn kết nối lớp học này với cái gì?
FRR

23
cause.isNetworkError () là NỮA : sử dụngerror.getKind() == RetrofitError.Kind.NETWORK
Hugo Gresse

6

Đối với trang bị thêm 1

Khi bạn gặp Throwablelỗi từ yêu cầu http của mình, bạn có thể phát hiện xem đó có phải là lỗi mạng hay không bằng một phương pháp như sau:

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

5

chỉ cần làm điều này, bạn sẽ được thông báo ngay cả đối với các vấn đề như

UnknownHostException

,

SocketTimeoutException

và những người khác.

 @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

bạn có thể sử dụng mã này

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

Thực hiện trong các phương pháp của bạn

        @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);        
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.