RxJava:数据库和远程服务器

时间:2016-10-22 04:29:17

标签: android rx-java retrofit2

我有一个对象列表,我希望从本地数据库(如果可用)检索,否则从远程服务器检索。我使用RxJava Observables(SqlBrite用于数据库,Retrofit用于远程服务器)。

我的查询代码如下:

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没有运行,没有网络请求。如果我切换observable的顺序,或者只是返回远程observable,它会按预期工作,它会命中远程服务器并返回对象列表。

为什么远程observable在这种情况下无法运行?当我首先将observable与db连接并再次改造时,不会调用订阅者的onNext,orError和onComplete方法。

谢谢!

2 个答案:

答案 0 :(得分:4)

Kaushik Gopal在他的RxJava-Android-Samples github项目中解决了这个问题。

他建议使用这种技术:

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

              ...
          });

在您的情况下,它可能如下所示:

remoteObservable
      .publish(network ->
                     Observable.merge(network,
                                      dbObservable.takeUntil(network)))
      .first(myObjects -> !myObjects.isEmpty());

编辑:听起来你可能需要这个:

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

答案 1 :(得分:2)

我假设您的观察结果可以从本地和远程获取数据,如下所示:

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

然后你可以映射它们并获得第一个意味着如果本地可用则使用本地如果不使用远程:

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

订阅如下:

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

有关更多详细信息或示例,您可以查看我的github回购: https://github.com/savepopulation/wikilight

祝你好运!

编辑:

您可以尝试下面的本地观察。只需检查是否有记录并返回空的observable。

@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意味着observable返回null但仍然可以观察到。当你使用concat返回observable.empty时,首先这意味着observable不能从本地发出任何东西,所以它可以从远程发射。