Android RxJava:数据库和远程服务器
我有一个要从本地数据库(如果可用)或远程服务器(否则)检索的对象列表。我将RxJava Observables SqlBrite用于数据库,并对远程服务器进行改造 我的查询代码如下: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
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查询将返回一个未缓存或已过期的空列表,因此我希望有一种方法,仅当第一个返回空列表时,才运行第二个可观察的。我认为第一个带有条件函数的操作符可以做到这一点,但在我的原始代码中从未运行过可观察的网络,即使缓存是空的。有什么想法吗?嗯,是的,我编辑了我的帖子。看看这是否适合你。谢谢,这是一个简洁的解决方案。我仍然不明白为什么我原来的解决方案不起作用,因此我需要研究它,但这个解决方案非常适合我的需要。再次感谢。