Realm, RxJava, asObservable () y doOnUnsubscribe ()

En mis proyectos de Android, uso realm como mi motor de almacenamiento de datos. ¡Me encanta!
También uso RxJava porque hace "threading" mucho más fácil, y me gusta mucho la "mentalidad reactiva". ¡Me encanta!

Utilizo un patrón MVP + algunas ideas de "arquitectura limpia" para crear mis aplicaciones.

Mis Interactors son los únicos que saben sobre Realm . Expongo datos con la ayuda de Observable, como esto:

 @Override public Observable<City> getHomeTown() { final Realm realm = Realm.getDefaultInstance(); return realm.where(City.class).equalTo("name", "Cluj-Napoca").findAllAsync().asObservable() .doOnUnsubscribe(new Action0() { @Override public void call() { realm.close(); } }) .compose(new NullIfNoRealmObject<City>()); } 

El problema es que mi doOnUnsubscribe secundario doOnUnsubscribe se llama antes de que Realm pueda hacer su cosa, manejando lo expuesto observable:

 Caused by: java.lang.IllegalStateException: This Realm instance has already been closed, making it unusable. at io.realm.BaseRealm.checkIfValid(BaseRealm.java:344) at io.realm.RealmResults.removeChangeListener(RealmResults.java:818) at io.realm.rx.RealmObservableFactory$3$2.call(RealmObservableFactory.java:137) at rx.subscriptions.BooleanSubscription.unsubscribe(BooleanSubscription.java:71) at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:124) at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:113) at rx.Subscriber.unsubscribe(Subscriber.java:98) at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:124) at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:113) at rx.Subscriber.unsubscribe(Subscriber.java:98) at rx.subscriptions.CompositeSubscription.unsubscribeFromAll(CompositeSubscription.java:150) at rx.subscriptions.CompositeSubscription.unsubscribe(CompositeSubscription.java:139) at ro.tudorluca.realm.sandbox.city.CityPresenter.onDestroy(CityPresenter.java:62) at ro.tudorluca.realm.sandbox.city.CityActivity.onDestroy(CityActivity.java:35) 

He creado un proyecto de caja de seguridad para este caso de uso.

Realmente me gusta usar Realm + RxJava, pero parece que no puedo encontrar una solución limpia para close la instancia de Realm cuando me unsubscribe (normalmente me doy de baja cuando la actividad se destruye). ¿Algunas ideas?

Editar 1 : https://github.com/realm/realm-java/issues/2357
Editar 2 : gracias al equipo de dominio muy activo, ya hay una solicitud de extracción para solucionar este problema.

3 Solutions collect form web for “Realm, RxJava, asObservable () y doOnUnsubscribe ()”

21 horas más tarde y esto es lo que me ocurrió:

 @Override public Observable<City> getHomeTown() { return getManagedRealm() .concatMap(new Func1<Realm, Observable<City>>() { @Override public Observable<City> call(Realm realm) { return realm.where(City.class).equalTo("name", "Cluj-Napoca").findAllAsync().asObservable() .compose(new NullIfNoRealmObject<City>()); } }); } private static Observable<Realm> getManagedRealm() { return Observable.create(new Observable.OnSubscribe<Realm>() { @Override public void call(final Subscriber<? super Realm> subscriber) { final Realm realm = Realm.getDefaultInstance(); subscriber.add(Subscriptions.create(new Action0() { @Override public void call() { realm.close(); } })); subscriber.onNext(realm); } }); } 

He intentado algo como esto antes de publicar la pregunta en stackoverflow, pero mi error fue usar flatMap() , en lugar de concatMap() .

A diferencia de flatMap() , concatMap() mantendrá el orden de las emisiones que, en mi caso, significa que mi Action0 -> realm.close() será la última acción que se llama después de cancelar la suscripción de la secuencia, Action0 -> results.removeChangeListener(listener) que estaba causando el problema.

Un ejemplo completo se puede encontrar en github .

Editar : gracias al equipo de dominio muy activo, ya hay una solicitud de extracción para solucionar este problema.

Dado que usted dijo que sólo el Interactor "sabe" sobre el marco Realm diría que ni siquiera devolver un objeto gestionado Realm, en su lugar devolver una copia no administrada de los resultados utilizando copyFromRealm . De esta manera, no tiene que preocuparse por la instancia de Realm abierta o cerrada en el presentador .

Al mismo tiempo, dejaría que el Presentador eligiera si la llamada debería ser asíncrona o no, ya que RxJava hace eso bastante fácil y no tendrás problemas llamando al método de carga de Interactor dentro de otro hilo (lo cual puede evitarse usando Loopers pero ¿Por qué complicar la situación si se puede hacer más simple: P).

Así que yo iría para:

 Override public Observable<City> getHomeTown() { final Realm realm = Realm.getDefaultInstance(); City city = realm.where(City.class).equalTo("name", "Cluj-Napoca").findFirst(); // make sure we don't send back Realm stuff, this is a deep copy that will copy all referenced objects (as the method doc says) City cityUnmanaged = realm.copyFromRealm(city); // safe to close the realm instance now realm.close(); return Observable.just(cityUnmanaged); } 

Tengo curiosidad por ver más opciones :).

Según mi opinión, una de las cosas más importantes para cuidar en una buena arquitectura es la modularidad. Todos los módulos principales (o bibliotecas) deben estar aislados del resto del código. Dado que Realm, RealmObject o RealmResult no se pueden pasar a través de subprocesos, es aún más importante que las operaciones relacionadas con Realm & Realm estén aisladas del resto del código.

Manteniendo esta filosofía en mente, me propuse el siguiente enfoque.

Para cada clase jsonModel, creamos una clase realmModel y una clase DAO (Data Access Object). La idea aquí es que, aparte de la clase DAO, ninguna clase de la clase debe conocer o acceder a entidades realmModel o Realm. La clase DAO toma jsonModel, lo convierte en realmModel, realiza operaciones de lectura / escritura / edición / eliminación y para operaciones de lectura DAO convierte realmModel a jsonModel y las devuelve.

De esta manera es fácil mantener Realm, evitar todos los problemas relacionados con el hilo, fáciles de probar y depurar.

Aquí hay un artículo sobre las mejores prácticas de Realm con una buena arquitectura https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f

También un proyecto de ejemplo que demuestra la integración de Realm en Android con MVP (Model View Presenter), RxJava, Retrofit, Dagger, Annotations & Testing. https://github.com/viraj49/Realm_android-injection-rx-test

  • Manejar Error de red con Retrofit observable
  • Schedulers.io () no vuelve al hilo principal
  • RxJava, buen caso de uso de flatmap
  • Encadenamiento de llamadas de Retrofit con RxJava y devolución del objeto principal
  • Android RxJava 2 Prueba JUnit - getMainLooper en android.os.Looper no se burla de RuntimeException
  • Encadenamiento de RxJava observables con callbacks / listeners
  • OnNext comience otro Observable
  • RxAndroid filter Observable <List <Item >>
  • Introducción a RxJava con sqlite
  • Prueba de unidad de presentador con RxJava CompositeSubscription
  • Proguard y RxAndroid V1.1.0
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.