Actualización de datos mediante SQLBrite + Retrofit
Aquí está mi caso de uso:
Estoy desarrollando una aplicación que se comunica con un servidor a través de una API REST y almacena los datos recibidos en una base de datos SQLite (se utiliza como un caché de algunos tipos).
- Prueba de unidad de presentador con RxJava CompositeSubscription
- Retrofit2 y kotlin
- Rx Observable emitiendo valores periódicamente
- Okhttp ignora la configuración de Dispatcher cuando se usa con Retrofit RxJavaCallAdapterFactory
- ¿Cómo crear un Observable en Android?
Cuando el usuario abre una pantalla, debe ocurrir lo siguiente:
- Los datos se cargan desde el DB, si están disponibles.
- La aplicación llama a la API para actualizar los datos.
- El resultado de la llamada API se mantiene en la base de datos.
- Los datos se vuelven a cargar desde el DB cuando se intercepta la notificación de cambio de datos.
Esto es muy similar al caso presentado aquí , pero hay una ligera diferencia.
Puesto que estoy usando SQLBrite, los observables de la base de datos no terminan (porque hay un ContentObserver
registrado allí, que empuja nuevos datos abajo de la corriente), así que los métodos como el concat
, la merge
, el etc. no trabajarán.
Actualmente, he resuelto esto utilizando el siguiente enfoque:
Observable.create(subscriber -> { dbObservable.subscribe(subscriber); apiObservable .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .subscribe( (data) -> { try { persistData(data); } catch (Throwable t) { Exceptions.throwOrReport(t, subscriber); } }, (throwable) -> { Exceptions.throwOrReport(throwable, subscriber); }) })
Parece que funciona bien, pero no parece elegante y "correcto".
¿Puede sugerirme o indicarme un recurso que explique cuál es la mejor manera de manejar esta situación?
- No se puede eliminar la acción javaCompile antigua, tal vez el nombre de la clase ha cambiado
- RxJava solo planificador de hilos de fondo
- Manera eficiente de manipular los subprocesos RxJava
- Desbordamiento de pila al utilizar Retrofit rxjava concatWith
- RxJava, ejecuta código en el hilo del observador antes de encadenar dos observables
- Operadores RxJava
- Cómo utilizar rxandroid para escuchar la actualización de ubicación de gps
- RxJava, buen caso de uso de flatmap
La solución a su problema es en realidad muy fácil y limpio si cambia la forma de pensar un poco. Estoy utilizando la misma interacción de datos (Retrofit + Sqlbrite) y esta solución funciona perfectamente.
Lo que tienes que hacer es usar dos suscripciones observables separadas, que se ocupan de procesos completamente diferentes.
-
Database
->
View
: Este se utiliza para adjuntar suView
(Activity
,Fragment
o lo que sea muestra sus datos) a los datos persistentes en db. Usted se suscribe a él UNA VEZ para laView
creada.
dbObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { displayData(data); }, throwable -> { handleError(throwable); });
-
API
->
Database
: El otro para buscar los datos de api y persistir en el db. Usted se suscribe a ella cada vez que desee actualizar sus datos en la base de datos.
apiObservable .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .subscribe(data -> { storeDataInDatabase(data); }, throwable -> { handleError(throwable); });
EDITAR:
Usted no quiere "transformar" ambos observables en uno, puramente por la razón que ha incluido en su pregunta. Ambos observables actúan de manera completamente diferente.
El observable
de Retrofit actúa como un Single
. Hace lo que necesita hacer, y termina (con onCompleted
).
El observable
de Sqlbrite es un Observable
típico, él emitirá algo cada vez que una tabla específica cambia. Teóricamente debería terminar en el futuro.
Ofc usted puede trabajar alrededor de esa diferencia, pero le llevaría lejos, lejos de tener un código limpio y fácilmente legible.
Si realmente, realmente necesita exponer una sola observable
, sólo puede ocultar el hecho de que está realmente suscribirse a la observable de retrofit al suscribirse a su base de datos.
- Envuelva la suscripción de Api en un método:
public void fetchRemoteData() { apiObservable .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .subscribe(data -> { persistData(data); }, throwable -> { handleError(throwable); }); }
-
fetchRemoteData
en la suscripción
dbObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe(() -> fetchRemoteData()) .subscribe(data -> { displayData(data); }, throwable -> { handleError(throwable); });
Te sugiero que realmente pienses en todo eso. Porque el hecho de que te estás forzando a la posición en la que necesitas un solo observable, podría estar restringiéndote bastante. Creo que esta será la cosa exacta que te obligará a cambiar tu concepto en el futuro, en lugar de protegerte del cambio mismo.