Realm,RxJava,asObservable()和doOnUnsubscribe()

时间:2016-02-26 20:47:45

标签: android rx-java mvp realm

在我的Android项目中,我使用realm作为我的数据存储引擎。我喜欢它!
我也使用RxJava,因为它使“线程化”变得如此简单,我真的很喜欢整个“反应性思维”。我喜欢它!

我使用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副作用在Realm能够做到这一点之前被调用,处理暴露的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)

我为此用例创建了一个sandbox项目。

我真的很喜欢使用Realm + RxJava,但是当close(我通常在活动被销毁时取消订阅)时,我似乎无法找到unsubscribe Realm实例的干净解决方案。有什么想法吗?

修改1 https://github.com/realm/realm-java/issues/2357
编辑2 :感谢非常活跃的领域团队,已经有pull request来解决此问题。

3 个答案:

答案 0 :(得分: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)

可以在github上找到完整的示例。

修改:感谢非常活跃的领域团队,已经有pull request来解决此问题。

答案 1 :(得分:0)

由于您说只有 Interactor “知道”有关Realm框架的信息我会说甚至不返回托管的Realm对象,而是使用copyFromRealm返回结果的非托管副本。这样您就不必关心 Presenter 中正在打开或关闭的Realm实例。

与此同时,我会让 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);
}

我很想看到更多选择:)。

答案 2 :(得分:0)

按照我的说法,在一个好的架构中要照顾的主要事情之一就是模块化。所有主要模块(或库)都应与其余代码隔离。由于Realm,RealmObject或RealmResult不能跨线程传递,因此制作Realm&amp;与其余代码隔离的领域相关操作。

牢记这一理念,我提出了以下方法。

对于每个jsonModel类,我们创建一个realmModel类和一个DAO(数据访问对象)类。这里的想法是除了DAO类之外,没有类必须知道或访问realmModel或Realm实体。 DAO类接受jsonModel,将其转换为realmModel,执行读/写/编辑/删除操作&amp;对于读取操作,DAO将生成的realmModel转换为jsonModel并返回它们。

这样很容易维护Realm,避免所有与线程相关的问题,易于测试和调试。

这是一篇关于Realm最佳实践的文章,其中有一个很好的架构https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f

此外,一个示例项目演示了Android上的Realm与MVP(模型视图演示者),RxJava,Retrofit,Dagger,Annotations&amp;测试。 https://github.com/viraj49/Realm_android-injection-rx-test