RxJava2 + Retrofit 2. Modificamos el adaptador para manejar la falta de estado de Internet en Android



Muy a menudo es necesario hacer solicitudes repetidas a la red, por ejemplo, cuando el usuario no ten铆a Internet y quer铆a recibir datos de Internet. Ser铆a bueno volver a lanzar la solicitud cuando aparezca. Es una buena pr谩ctica mostrarle al usuario una IU espec铆fica que le explique lo que sucedi贸 y permita que la solicitud se vuelva a lanzar. Agregar esa l贸gica puede ser bastante doloroso, especialmente cuando tenemos una gran cantidad de clases ViewModel. Por supuesto, puede implementar la l贸gica de nueva consulta en cada clase de ViewModel, pero esto no es conveniente y hay una gran probabilidad de errores.

驴Hay alguna manera de hacer este procesamiento solo una vez?


Afortunadamente, RxJava2 y Retrofit2 lo permiten.

Ya hay varias soluciones en Stackoverflow:

1. Creando tu propia CallAdapterFactory (m谩s informaci贸n aqu铆 )
2. Repita la cadena usando PublishSubject (m谩s informaci贸n aqu铆 )

La primera soluci贸n usa RxJava1, ya est谩 desactualizada y tambi茅n simplemente repite la cadena varias veces, sin reaccionar ante la ocurrencia del evento. La segunda soluci贸n es buena, pero necesitamos usar el reintentoCuando el operador en cada cadena. Y as铆, combin茅 las dos soluciones en una.

Implementaci贸n


Creemos un proyecto simple. Coloque dos pesta帽as en la pantalla principal. Cada uno de ellos muestra un texto que mostrar谩 cu谩ntos elementos carga la API. Si se produce un error durante la ejecuci贸n, mostramos una SnackBar con un bot贸n Intentar de nuevo.



Definimos clases base como BaseActivity, BaseFragment, BaseViewModel, son necesarias para implementar la l贸gica de la repetici贸n de solicitudes en un lugar y evitar la duplicaci贸n de este c贸digo. Crea dos fragmentos que extender谩n BaseFragment. Cada fragmento colocado tiene su propio ViewModel y realiza solicitudes de forma independiente a la API. Cre茅 estos fragmentos para mostrar que si se produce un error, se repetir谩 cada solicitud. A continuaci贸n, cree una f谩brica RxRetryCallAdapterFactory que extienda CallAdapter.Factory. Despu茅s de eso, cree una instancia de RxJava2CallAdapterFactory. Necesitamos esta instancia para acceder al RxJava2CallAdapter, ya que no queremos duplicar el c贸digo que ya est谩 en la biblioteca Retrofit. Adem谩s, creemos un m茅todo est谩tico que devolver谩 una instancia de nuestra f谩brica. C贸digo de muestra a continuaci贸n:

class RxRetryCallAdapterFactory : CallAdapter.Factory() { companion object { fun create() : CallAdapter.Factory = RxRetryCallAdapterFactory() } private val originalFactory = RxJava2CallAdapterFactory.create() override fun get(returnType : Type, annotations : Array<Annotation>, retrofit : Retrofit) : CallAdapter<*, *>? { val adapter = originalFactory.get(returnType, annotations, retrofit) ?: return null return RxRetryCallAdapter(adapter) } } 

A continuaci贸n, cree un RxRetryCallAdapter que implemente la interfaz CallAdapter y necesitemos pasar la instancia de CallAdapter al constructor. De hecho, debe ser una instancia de RxJava2CallAdapter, que devuelve la f谩brica original.

Luego, necesitamos implementar las siguientes cosas:

  • retryCuando la instrucci贸n utilizada para implementar la funcionalidad de repetici贸n
  • sentencia doOnError () que maneja los errores. Se utiliza para enviar la transmisi贸n, que se procesa en BaseActivity y muestra la SnackBar al usuario.
  • PublishSubject se utiliza como desencadenante de eventos que vuelve a firmar la cadena.
  • Operador observeOn (Schedulers.io ()) que debe aplicarse a PublishSubject (si no se agrega esta l铆nea, la suscripci贸n se realizar谩 en el hilo principal y obtendremos una NetworkOnMainThreadException
  • Transformamos PublishSubject en Flowable y establecemos BackpressureStrategy.LATEST, ya que solo necesitamos el 煤ltimo error

Nota: Para proporcionar PublishSubject, cre茅 una clase singleton simple que expone todas las dependencias singleton en el proyecto. En un proyecto real, es probable que use un marco de inyecci贸n de dependencia como Dagger2

 class RxRetryCallAdapter<R>(private val originalAdapter : CallAdapter<R, *>) : CallAdapter<R, Any> { override fun adapt(call : Call<R>) : Any { val adaptedValue = originalAdapter.adapt(call) return when (adaptedValue) { is Completable -> { adaptedValue.doOnError(this::sendBroadcast) .retryWhen { AppProvider.provideRetrySubject().toFlowable(BackpressureStrategy.LATEST) .observeOn(Schedulers.io()) } } is Single<*> -> { adaptedValue.doOnError(this::sendBroadcast) .retryWhen { AppProvider.provideRetrySubject().toFlowable(BackpressureStrategy.LATEST) .observeOn(Schedulers.io()) } } //same for Maybe, Observable, Flowable else -> { adaptedValue } } } override fun responseType() : Type = originalAdapter.responseType() private fun sendBroadcast(throwable : Throwable) { Timber.e(throwable) LocalBroadcastManager.getInstance(AppProvider.appInstance).sendBroadcast(Intent(BaseActivity.ERROR_ACTION)) } } 

Cuando el usuario hace clic en el bot贸n Intentar de nuevo, llamamos al siguiente PublishSubject. Despu茅s de eso, nos volvemos a suscribir a la cadena rx.

Prueba


Apague Internet y ejecute la aplicaci贸n. El n煤mero de elementos cargados es cero en cada pesta帽a y SnackBar muestra un error. Encienda Internet y haga clic en Probar Adain. Despu茅s de unos segundos, el n煤mero de elementos cargados cambia en cada una de las pesta帽as.



Si alguien lo necesita, entonces el c贸digo fuente est谩 aqu铆

Source: https://habr.com/ru/post/442072/


All Articles