Android RxJava:数据库和远程服务器

Android RxJava:数据库和远程服务器,android,rx-java,retrofit2,Android,Rx Java,Retrofit2,我有一个要从本地数据库(如果可用)或远程服务器(否则)检索的对象列表。我将RxJava Observables SqlBrite用于数据库,并对远程服务器进行改造 我的查询代码如下: Observable<List<MyObject>> dbObservable = mDatabase .createQuery(MyObject.TABLE_NAME,MyObject.SELECT_TYPE_A) .mapToList(My

我有一个要从本地数据库(如果可用)或远程服务器(否则)检索的对象列表。我将RxJava Observables SqlBrite用于数据库,并对远程服务器进行改造

我的查询代码如下:

Observable<List<MyObject>> dbObservable = mDatabase
            .createQuery(MyObject.TABLE_NAME,MyObject.SELECT_TYPE_A)
            .mapToList(MyObject.LOCAL_MAPPER);
Observable<List<MyObject>> remoteObservable = mRetrofitService.getMyObjectApiService().getMyObjects();

return Observable.concat(dbObservable, remoteObservable)
    .first(new Func1<List<MyObject>, Boolean>() {
                @Override
                public Boolean call(List<MyObject> myObjects) {
                    return !myObjects.isEmpty();
                }
            });
我看到第一个observable正在运行,并用一个空列表命中了第一个方法,但随后改型observable没有运行,没有网络请求。如果我切换可观察对象的顺序,或者只是返回远程可观察对象,它会按预期工作,它会点击远程服务器并返回对象列表

为什么在这种情况下,远程可观测对象无法运行?当我先将观察值与db连接起来,然后再进行改装时,不会调用订阅服务器的onNext、orError和onComplete方法


谢谢

Kaushik Gopal在他的github项目中解决了这个问题

他建议使用这种技术:

getFreshNetworkData()
          .publish(network ->
                         Observable.merge(network,
                                          getCachedDiskData().takeUntil(network)))
          .subscribeOn(Schedulers.io())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(new Subscriber<List<MyObject>() {

              ...
          });
编辑:听起来你可能需要这个:

dbObservable
    .flatMap(localResult -> {
        if (localResult.isEmpty()) {
            return remoteObservable;
        } else {
            return Observable.just(localResult);
        }
    });

我假设你有可以从本地和远程获取数据的观测值,如下所示:

        final Observable<Page> localResult = mSearchLocalDataSource.search(query);
        final Observable<Page> remoteResult = mSearchRemoteDataSource.search(query)
                .doOnNext(new Action1<Page>() {
                    @Override
                    public void call(Page page) {
                        if (page != null) {
                            mSearchLocalDataSource.save(query, page);
                            mResultCache.put(query, page);
                        }
                    }
                });
mCompositeSubscription.clear();
        final Subscription subscription = mSearchRepository.search(this.mQuery)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Page>() {
                    @Override
                    public void onCompleted() {
                        // Completed
                    }

                    @Override
                    public void onError(Throwable e) {
                        mView.onDefaultMessage(e.getMessage());
                    }

                    @Override
                    public void onNext(Page page) {
                        mView.onDefaultMessage(page.getContent());
                    }
                });

        mCompositeSubscription.add(subscription);
然后您可以映射它们并首先获取,这意味着如果本地可用,请使用本地,如果不使用远程:

        return Observable.concat(localResult, remoteResult)
                .first()
                .map(new Func1<Page, Page>() {
                    @Override
                    public Page call(Page page) {
                        if (page == null) {
                            throw new NoSuchElementException("No result found!");
                        }
                        return page;
                    }
                });
并按如下方式订阅:

        final Observable<Page> localResult = mSearchLocalDataSource.search(query);
        final Observable<Page> remoteResult = mSearchRemoteDataSource.search(query)
                .doOnNext(new Action1<Page>() {
                    @Override
                    public void call(Page page) {
                        if (page != null) {
                            mSearchLocalDataSource.save(query, page);
                            mResultCache.put(query, page);
                        }
                    }
                });
mCompositeSubscription.clear();
        final Subscription subscription = mSearchRepository.search(this.mQuery)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Page>() {
                    @Override
                    public void onCompleted() {
                        // Completed
                    }

                    @Override
                    public void onError(Throwable e) {
                        mView.onDefaultMessage(e.getMessage());
                    }

                    @Override
                    public void onNext(Page page) {
                        mView.onDefaultMessage(page.getContent());
                    }
                });

        mCompositeSubscription.add(subscription);
有关更多详细信息或示例,请查看my github repo:

祝你好运

编辑:

您可以尝试下面这样的局部观察。它只是检查是否有记录,并返回一个空的可观测值

@Override
public Observable<Page> search(@NonNull final String query) {
    return Observable.create(new Observable.OnSubscribe<Page>() {
        @Override
        public void call(Subscriber<? super Page> subscriber) {
            final Realm realm = Realm.getInstance(mRealmConfiguration);
            final Page page = realm.where(Page.class)
                    .equalTo("query", query)
                    .findFirst();
            if (page != null && page.isLoaded() && page.isValid()) {
                Log.i("data from", "realm");
                subscriber.onNext(realm.copyFromRealm(page));
            } else {
                Observable.empty();
            }
            subscriber.onCompleted();
            realm.close();
        }
    });
}
编辑2:


当您从本地concat返回null时,首先将不起作用,并且不会调用远程,因为null意味着可观察返回null,但仍然可以观察。当您使用concat和first返回observable.empty时,这意味着observable不能从本地发射任何东西,因此它可以从远程发射。

谢谢,这与我尝试的类似,但正如本文所述,它不会起作用,因为我们无条件使用第一个运算符,我们将始终从数据库返回结果,即使是空的,数据库操作员也总是调用onNext,如果数据库中没有任何内容,它只会返回一个空列表,但仍会调用onNext,因此第一个操作员将阻止网络observable运行。我不确定您是否理解正确,但我编辑了我的答案。如果没有记录,则从db返回空的observable,则会调用网络调用。尽管对observable.empty的调用是冗余的,但此解决方案仍然有效。然而,我想理解为什么将concat与firstcondition一起使用并不能达到预期效果。我编辑了我的答案。我想这会回答你的问题。谢谢。如果我想在网络上查找每个请求的新数据,这个解决方案效果很好,但是在我的例子中,缓存的项目有一个过期日期,所以我只想在项目从未缓存或过期的情况下访问网络。在这种情况下,我的DB查询将返回一个未缓存或已过期的空列表,因此我希望有一种方法,仅当第一个返回空列表时,才运行第二个可观察的。我认为第一个带有条件函数的操作符可以做到这一点,但在我的原始代码中从未运行过可观察的网络,即使缓存是空的。有什么想法吗?嗯,是的,我编辑了我的帖子。看看这是否适合你。谢谢,这是一个简洁的解决方案。我仍然不明白为什么我原来的解决方案不起作用,因此我需要研究它,但这个解决方案非常适合我的需要。再次感谢。