2016-02-26 10 views
12

In meinen Android-Projekten verwende ich als meine Datenspeicher-Engine. Ich liebe es!
Ich benutze auch RxJava, weil es "Threading" so viel einfacher macht, und ich mag wirklich die ganze "reaktive Denkweise". Ich liebe es!
Realm, RxJava, asObservable() und doOnUnsubscribe()

Ich benutze ein MVP-Muster + einige "saubere Architektur" Ideen, um meine Apps zu bauen.

Meine Interactors sind die einzigen, die über Realm wissen. Ich aussetzen Daten mit Hilfe von beobachtbaren, wie folgt aus:

@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>()); 
} 

Das Problem ist meine doOnUnsubscribe Nebeneffekt vor Realm aufgerufen wird, seine Sache zu tun, die freiliegende Handhabung beobachtbar:

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) 

I erstellt ein sandbox Projekt für diesen Anwendungsfall.

Ich mag wirklich Realm + RxJava, aber ich kann keine saubere Lösung zu close der Realm-Instanz zu finden, wenn ich unsubscribe (Ich normalerweise abmelden, wenn die Aktivität zerstört wird). Irgendwelche Ideen?

Edit 1: https://github.com/realm/realm-java/issues/2357
Edit 2: Dank des sehr aktiven Bereich Team, gibt es bereits ein pull request dieses Problem zu beheben.

Antwort

1

21 Stunden später und das ist, was ich kam mit:

@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); 
     } 
    }); 
} 

Ich habe versucht, so etwas wie diese, bevor die Frage auf Stackoverflow veröffentlichen, aber mein Fehler flatMap() wurde, daß anstelle von concatMap().

Im Gegensatz zu flatMap(), concatMap() wird die Reihenfolge der Emissionen, die in meinem Fall bedeutet, dass meine Action0 -> realm.close() wird die letzte Aktion sein aus dem Strom nach Abbestellung genannt wird, nach Realm Action0 -> results.removeChangeListener(listener), das das Problem verursacht wurde.

Ein vollständiges Beispiel finden Sie unter github.

Bearbeiten: Dank dem sehr aktiven Realm-Team gibt es bereits eine pull request, um dieses Problem zu beheben.

+0

Erwägen, Realm über Observable # zu umhüllen, mit, dies wird Ihre getManagedRealm() Methode vereinfachen: http://pastebin.com/CrzryvCq –

+0

Ich versuche Ihre Lösung und ich konfrontiert Problem: zu viele in Realm-Datei öffnen –

0

Da sagen Sie, dass nur die Interactor über den Realm Rahmen „wissen“, würde ich sagen, nicht einmal ein verwaltetes Reich-Objekt zurückgeben, anstatt eine nicht verwaltete Kopie der Ergebnisse zurückgeben copyFromRealm verwenden. Auf diese Weise müssen Sie sich nicht darum kümmern, dass die Realm-Instanz im Presenter geöffnet oder geschlossen ist.

Zugleich würde ich die Presenter lassen entschied, ob der Anruf asynchron erfolgen soll oder nicht, da RxJava tut das ziemlich cool und einfach und Sie werden keine Probleme haben die Interactor Load-Methode in einem anderen Thread aufrufen (was mit Looper vermieden werden kann, aber warum die Situation komplizierter macht, wenn man es einfacher machen kann: P).

So würde ich mich für:

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); 
} 

Ich bin gespannt, um mehr Optionen zu sehen :).

+1

Zurückgeben eines beobachtbaren , mein Interactor umfasst vollständig die "reaktive Denkweise". Wenn Sie die Abfrage ausführen, gibt der Interactor einen Stream zurück, der die Daten überträgt, wenn sie bereit und verfügbar sind. Wenn neue Daten verfügbar sind, ist der zurückgegebene Stream bereit, diese Daten zu übertragen. In meinem Fall werde ich jedes Mal eine aktualisierte Stadt bekommen, wenn jemand anders Änderungen an Realm vorgenommen hat. "Auto-Refresh" ist eine Kernfunktion von Realm und eine sehr nette. –

+0

CopyFromRealm() ist auch ok für meine Demo, weil meine Objekte klein sind und keine Beziehungen haben. Aber im wirklichen Leben habe ich einen komplexen Graphen von Objekten und ich möchte wirklich nicht alle in den Speicher laden. "Lazy + no-copy data" ist ein weiteres Kernmerkmal von Realm, das Sie nutzen sollten, wann immer Sie können. –

+0

Realm hat eine "Threading" -Begrenzung und Sie können RxJava nicht für die Verarbeitung von Multithread-Abfragen verwenden. Es gibt ein gutes Beispielprojekt, das Sie für weitere Fallstricke erkunden können: https://github.com/realm/realm-java/blob/master/examples/rxJavaExample/src/main/java/io/realm/examples/rxjava/gotchas /GotchasActivity.java –

0

Für mich ist die Modularität eines der wichtigsten Dinge in einer guten Architektur. Alle Hauptmodule (oder Bibliotheken) sollten vom Rest des Codes isoliert werden. Da Realm, RealmObject oder RealmResult nicht über Threads übergeben werden können, ist es noch wichtiger, Realm & Realm-bezogene Operationen vom Rest des Codes zu isolieren.

Um diese Philosophie im Hinterkopf zu behalten, kam ich auf den folgenden Ansatz.

Für jede jsonModel-Klasse erstellen wir eine realmModel-Klasse und eine DAO-Klasse (Data Access Object). Die Idee hier ist, dass außer der DAO-Klasse keine der Klasse realmModel oder Realm-Entitäten kennen oder darauf zugreifen darf. Die DAO-Klasse verwendet jsonModel, konvertiert sie in realmModel, führt Lese-/Schreib-/Bearbeitungs-/Löschvorgänge durch & für Leseoperationen. DAO konvertiert das resultierende realmModel in jsonModel und gibt sie zurück.

Auf diese Weise ist es einfach, Realm zu verwalten, alle threadbezogenen Probleme zu vermeiden, einfach zu testen und zu debuggen. Hier

ist ein Artikel über Realm Best Practices mit einem guten architechture https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f

auch ein Beispielprojekt demonstriert Integration von Realm auf Android mit MVP (Model-View-Presenter), RxJava, Retrofit, Dolch, Anmerkungen & Testing. https://github.com/viraj49/Realm_android-injection-rx-test

Verwandte Themen