Как я должен обрабатывать "Нет подключения к Интернету" с помощью Retrofit на Android
Я хотел бы обрабатывать ситуации, когда нет подключения к Интернету. Обычно я запускал:
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
(из здесь) перед отправкой запросов в сеть и уведомлять пользователя, если интернет-соединение не было.
Из того, что я видел, Retrofit не справляется с этой ситуацией конкретно. Если нет подключения к интернету, я просто получу RetrofitError
с таймаутом в качестве причины.
Если я хотел бы включить этот вид проверки в каждый HTTP-запрос с помощью Retrofit, как мне это сделать? Или я должен делать это вообще.
Спасибо
Алекс
Ответы
Ответ 1
В результате я создаю настраиваемый клиент 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(), ...))
Ответ 2
Так как retrofit 1.8.0
это устарело
retrofitError.isNetworkError()
вы должны использовать
if (retrofitError.getKind() == RetrofitError.Kind.NETWORK)
{
}
существует несколько типов ошибок, которые вы можете обрабатывать:
NETWORK
При общении с сервером произошло сообщение об ошибке IOException. Тайм-аут, Нет соединения и т.д.
CONVERSION
Исключение было выбрано, пока (de) сериализовало тело.
HTTP
Код состояния HTTP не-200 был получен с сервера, например. 502, 503 и т.д.
UNEXPECTED
При попытке выполнить запрос произошла внутренняя ошибка. Рекомендуется перебросить это исключение, чтобы ваше приложение вышло из строя.
Ответ 3
@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 not a network error ...]
}
}
}
Ответ 4
С помощью 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";
}
}
Ответ 5
Когда вы получаете сообщение об ошибке 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!";
}
}
}
UPDATE. Я должен сказать, что это работает только с Retrofit 1, а не с Retrofit 2.
Ответ 6
вы можете использовать этот код
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);
}