AlexV
Me gustaría manejar situaciones en las que no hay conexión a Internet. Por lo general, ejecutaría:
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
(de aquí) antes de enviar las solicitudes a la red y notificar al usuario si no hubiera conexión a Internet.
Por lo que vi, Retrofit no maneja esta situación específicamente. Si no hay conexión a Internet, solo obtendré RetrofitError
con el tiempo de espera como una razón.
Si quisiera incorporar este tipo de verificación en cada solicitud HTTP con Retrofit, ¿cómo debo hacerlo? O debería hacerlo en absoluto.
Gracias
Alex
Lo que terminé haciendo fue crear un cliente Retrofit personalizado que verifica la conectividad antes de ejecutar una solicitud y genera una excepción.
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);
}
}
y luego usarlo al configurar RestAdapter
RestAdapter.Builder().setEndpoint(serverHost)
.setClient(new ConnectivityAwareUrlClient(new OkHttpClient(), ...))
-
¿De dónde es su clase NetworkConnectivityManager? ¿Disfraz?
– NPike
03/04/2014 a las 17:58
-
Sí, personalizado. Básicamente es el código de la pregunta.
– AlexV
7 abr 2014 a las 10:34
-
OkHttpClient no funciona en lugar de usar OKClient (). BDW buena respuesta. gracias.
– Harshvardhan Trivedi
3 de diciembre de 2014 a las 11:08
-
Gracias :-). Editó su respuesta con el uso correcto de OkHttp.
– usuario1007522
13 de febrero de 2015 a las 9:56
-
@MominAlAziz dependiendo de cómo lo definas
NoConnectivityException
puedes hacer que se extiendaIOException
o hacer que se extiendaRuntimeException
– AlexV
25 de junio de 2015 a las 8:11
Muhammad Alfaifi
Desde la actualización 1.8.0
esto ha sido obsoleto
retrofitError.isNetworkError()
tienes que usar
if (retrofitError.getKind() == RetrofitError.Kind.NETWORK)
{
}
Hay varios tipos de errores que puede manejar:
NETWORK
Ocurrió una IOException mientras se comunicaba con el servidor, por ejemplo, Tiempo de espera, Sin conexión, etc.
CONVERSION
Se lanzó una excepción al (des) serializar un cuerpo.
HTTP
Se recibió un código de estado HTTP que no es 200 del servidor, por ejemplo, 502, 503, etc.
UNEXPECTED
Se produjo un error interno al intentar ejecutar una solicitud. Es una buena práctica volver a lanzar esta excepción para que su aplicación se bloquee.
-
Evitar el uso de
equals
sobre variables, siempre useCONSTANT.equals(variable)
en su lugar, para evitar posibles NullPointerException. O incluso mejor en este caso, las enumeraciones aceptan == comparación, por lo queerror.getKind() == RetrofitError.Kind.NETWORK
podría ser un mejor enfoque– MariusBudin
12 mayo 2017 a las 10:34
-
Reemplace Java con Kotlin si está cansado de NPE y otras limitaciones/dolor de sintaxis
– Luis CAD
25 de junio de 2018 a las 13:38
Kevin
Con Retrofit 2, usamos una implementación de OkHttp Interceptor para verificar la conectividad de la red antes de enviar la solicitud. Si no hay red, lanza una excepción según corresponda.
Esto permite manejar específicamente los problemas de conectividad de la red antes de presionar 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";
}
}
-
Es defectuoso si enciende el modo Avión y luego lo apaga porque solo está configurando el var una vez, lo que hace que el observable sea inútil.
–Oliver Dixon
24 de febrero de 2017 a las 18:47
-
Así que estás haciendo un
OkHttpClient.Builder.addInterceptor(new ConnectivityInterceptor(HERE))
¿Qué debería estar AQUÍ?– Rúmido
28 de abril de 2017 a las 14:45
-
¿Puedes incluir ese código para que la gente tenga una idea completa?
–Oliver Dixon
2 de mayo de 2017 a las 8:01
-
@Kevin, ¿cómo puedo asegurarme de que se actualizará una vez que la conexión esté disponible?
– Rúmido
8 de mayo de 2017 a las 9:31
-
esta debería ser la respuesta aceptada. más ejemplo de esto aquí: migapro.com/detect-offline-error-in-retrofit-2
– j2emanue
28 de junio de 2018 a las 16:48
KBog
Esto es lo que hice en API 29 & API 30
:
1. Creé una clase WiFiService simple que contendrá el administrador de conectividad:
class WifiService {
private lateinit var wifiManager: WifiManager
private lateinit var connectivityManager: ConnectivityManager
companion object {
val instance = WifiService()
}
fun initializeWithApplicationContext (context: Context) {
wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}
// Helper that detects if online
fun isOnline(): Boolean {
val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
if (capabilities != null) {
when {
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> return true
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> return true
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> return true
}
}
return false
}
}
2. Cree ConnectivityInterceptor para verificar el acceso a Internet:
class ConnectivityInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
if (!WifiService.instance.isOnline()) {
throw IOException("No internet connection")
} else {
return chain.proceed(chain.request())
}
}
}
3. Úselo en Retrofit2 de la siguiente manera:
class RestApi {
private val okHttpClient by lazy {
OkHttpClient.Builder()
.addInterceptor(ConnectivityInterceptor())
.build()
}
// Define all the retrofit clients
private val restApiClient by lazy {
Retrofit.Builder()
.baseUrl("http://localhost:10000")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
// ...
}
4. Finalmente, inicialice WifiService como tal:
class MainApplication: Application() {
companion object {
lateinit var instance: MainApplication
}
override fun onCreate() {
super.onCreate()
instance = this
setupServices()
}
private fun setupServices() {
WifiService.instance.initializeWithApplicationContext(this)
}
}
-
Hay un ErrorHandler proporcionado en la fuente Retrofit que puede usar. Si no maneja el error usted mismo, Retrofit le dará un RetrofitError de [ java.net.UnknownHostException: Unable to resolve host “example.com”: No address associated with hostname ]
– Codeversed
14 mayo 2014 a las 14:03
-
@Codeversed también lo es tener
isNetworkError
deshacerse de no poder resolver el error del host?– omnipresente
15 de mayo de 2014 a las 1:36
-
¿Cómo implementaría esto en su interfaz, cliente? Quiero decir, ¿a qué conectas esta clase?
– Frankelot
25 mayo 2014 a las 19:40
-
causa.isNetworkError() es obsoleto : usar
error.getKind() == RetrofitError.Kind.NETWORK
– Hugo Gresse
19 de febrero de 2015 a las 13:21
solo haga esto, se le notificará incluso por problemas como
UnknownHostException
,
Excepción de tiempo de espera de socket
y otros.
@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
} }
Olcay Ertas
Para reacondicionamiento 1
Cuando obtienes un Throwable
error de su solicitud http, puede detectar si se trata de un error de red con un método como este:
String getErrorMessage(Throwable e) {
RetrofitError retrofitError;
if (e instanceof RetrofitError) {
retrofitError = ((RetrofitError) e);
if (retrofitError.getKind() == RetrofitError.Kind.NETWORK) {
return "Network is down!";
}
}
}
David Hackro
puedes usar este codigo
Respuesta.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;
}
}
Implementación en sus métodos
@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);
}
Puede usar el bloque try-catch para detectar la excepción de tiempo de espera para la conexión http. Luego, informe a los usuarios sobre el estado de la conexión a Internet. No es bonito, pero es una solución alternativa.
– Tugrul
26 de diciembre de 2013 a las 14:48
Sí, pero es mucho más rápido verificar con Android si tiene conexión a Internet en lugar de esperar a que se agote el tiempo de conexión del socket.
– AlexV
26 de diciembre de 2013 a las 15:41
Android Query maneja el “sin internet” y devuelve el código de error correspondiente lo suficientemente pronto. ¿Tal vez valga la pena reemplazarlo con aQuery? Otra solución es crear un detector de cambios en la red y, por lo tanto, la aplicación sabrá sobre la disponibilidad de Internet antes de enviar la solicitud.
– Stan
26 de diciembre de 2013 a las 18:17
para reequipamiento 2, véase github.com/square/retrofit/issues/1260
– ghanbari
3 mayo 2016 a las 14:11