Realm, RxJava, asObservable () и doOnUnsubscribe ()

В моих проектах Android я использую область как механизм хранения данных. Я люблю это!
Я также использую RxJava, потому что он делает «threading» намного проще, и мне очень нравится весь «реактивный менталитет». Я люблю это!

Я использую шаблон MVP + некоторые идеи «чистой архитектуры» для создания моих приложений.

Мои Interactors – единственные, кто знает о Realm . Я выставляю данные с помощью Observable, например:

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

Проблема заключается в doOnUnsubscribe побочный эффект doOnUnsubscribe до того, как Realm сможет справиться с этим, обработав видимое:

 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) 

Я создал проект песочницы для этого варианта использования.

Мне очень нравится использовать Realm + RxJava, но я не могу найти чистого решения, чтобы close экземпляр Realm, когда я unsubscribe (обычно я отказываюсь от подписки, когда действие уничтожается). Есть идеи?

Изменить 1 : https://github.com/realm/realm-java/issues/2357
Редактирование 2 : благодаря очень активной команде области, уже есть запрос на растяжение, чтобы исправить эту проблему.

21 час спустя, и вот что я придумал:

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

Я попробовал что-то вроде этого, прежде чем публиковать вопрос о stackoverflow, но моя ошибка заключалась в использовании flatMap() вместо concatMap() .

В отличие от flatMap() , concatMap() будет поддерживать порядок выбросов, что в моем случае означает, что мое Action0 -> realm.close() будет последним действием, вызываемым после Action0 -> results.removeChangeListener(listener) подписки из потока, после действия Realm's Action0 -> results.removeChangeListener(listener) который вызывал проблему.

Полный пример можно найти на github .

Изменить : благодаря очень активной команде в сфере, уже есть запрос на извлечение, чтобы исправить эту проблему.

Поскольку вы сказали, что только Interactor «знает» об инфраструктуре Realm, я бы сказал, что даже не вернул управляемый объект Realm, вместо этого вернет неуправляемую копию результатов с помощью copyFromRealm . Таким образом, вам не нужно заботиться о том, чтобы экземпляр Realm был открыт или закрыт в Presenter .

В то же время я бы позволил Presenter выбрать, что вызов должен выполняться асинхронно или нет, так как RxJava делает это довольно круто и легко, и у вас не будет проблем, вызывающих метод загрузки Interactor в другом потоке (чего можно избежать с помощью Loopers, но Почему чрезмерно затруднять ситуацию, если вы можете сделать это проще: P).

Поэтому я бы пошел:

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

Мне любопытно увидеть больше вариантов :).

Как и я, одним из основных моментов, которые нужно учесть в хорошей архитектуре, является модульность. Все основные модули (или библиотеки) должны быть изолированы от остальной части кода. Поскольку Realm, RealmObject или RealmResult не могут быть переданы по потокам, еще важнее сделать операции, связанные с Realm & Realm, изолированными от остальной части кода.

Помня об этой философии, я придумал следующий подход.

Для каждого класса jsonModel мы создаем класс realmModel и класс DAO (Data Access Object). Идея здесь заключается в том, что кроме класса DAO ни один из классов не должен знать или получать доступ к объектам realmModel или Realm. Класс DAO принимает jsonModel, преобразует его в realmModel, выполняет операции чтения / записи / редактирования / удаления и для операций чтения. DAO преобразует приведенные realmModel в jsonModel и возвращает их.

Таким образом, легко поддерживать Realm, избегать всех проблем, связанных с потоком, легко тестировать и отлаживать.

Вот статья о лучших практиках Realm с хорошей архитектурой https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f

Также образец проекта, демонстрирующий интеграцию Realm на Android с MVP (Model View Presenter), RxJava, Retrofit, Dagger, Annotations & Testing. https://github.com/viraj49/Realm_android-injection-rx-test