switchIfEmpty(Observable emptyObservable)操作符从字面意思上就很好理解,就是当为空的时候跳转到emptyObservable。
那末如何理解当为空的时候
. 下面将会使用实际案例解释这个switchIfEmpty
的使用方法。
假设我们的app里有加载文章列表功能,要求加载的逻辑以下:加载文章的的时候,先从本地加载,如果本地存在就是用本地的数据,如果不存在从网络获得。
下面是业务代码:
//从本地数据获得文章列表
getArticlesObservable(pageIndex, pageSize, categoryId)
//本地不存在,要求api
.switchIfEmpty(articleApi.getArticlesByCategoryId(pageIndex + "", pageSize + "", categoryId + "")
.compose(this.<RespArticlePaginate>handlerResult())
.flatMap(new Func1<RespArticlePaginate, Observable<RespArticlePaginate>>() {
@Override
public Observable<RespArticlePaginate> call(RespArticlePaginate respArticlePaginate) {
if (respArticlePaginate != null && respArticlePaginate.getList() != null) {
try {
articleDao.insertOrReplaceInTx(respArticlePaginate.getList());
} catch (Exception e) {
e.printStackTrace();
}
}
return Observable.just(respArticlePaginate);
}
}))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(createSubscriber(ID_ARTICLE_LIST)))
这里的 createSubscriber
封装了Subscriber对成功、失败的数据处理,然后统1把数据推给上1层,就不用每一个地方都写下面相同的模板代码了:
observable.subscribe(new Action1<RespArticlePaginate>() {
@Override
public void call(RespArticlePaginate respArticlePaginate) {
//success data
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
// error data
}
})
那末createSubscriber
是如何实现的,先看subscribe方法源码 以下:
public final Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError) {
if (onNext == null) {
throw new IllegalArgumentException("onNext can not be null");
}
if (onError == null) {
throw new IllegalArgumentException("onError can not be null");
}
Action0 onCompleted = Actions.empty();
return subscribe(new ActionSubscriber<T>(onNext, onError, onCompleted));
}
很简单,他是直接new了1个ActionSubscriber,然后把我们之前在代码里写的各个回调(onNext、onError、onComplete)当作参数传递进去。那末我们的createSubscriber
也能够摹拟它的实现:
/**
* 处理结果(分发结果) 封装
*
* @param id 辨别业务类型
*/
protected <T> ActionSubscriber<T> createSubscriber(final int id) {
//由于我们只关心onNext和onError
Action0 onCompleted = Actions.empty();
return new ActionSubscriber<T>(new Action1<T>() {
@Override
public void call(T t) {
pushSuccessData(id, t);
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
pushThrowable(id, throwable);
}
}, onCompleted);
}
好了,言归正传,回到我们上面提到的需求。根据需求我们来分析下代码:
getArticlesObservable
方法用来从本地获得文章列表,articleApi.getArticlesByCategoryId
方法是用来当本地不存在的时候从网络获得。仿佛这些代码可以实现了我们上面提到的需求了。而且很简洁。
实践是检验真谛的唯1标准,我们先运行下看看(本地环境是数据库没有文章列表)。
运行后,发现界面并没有展现数据,通过debug返现,代码履行了检测本地缓存的逻辑,且本地找不到符合逻辑的数据,也就是说从本地找到的结果为空。但是没有依照我们料想的是履行网络要求。
先来看看查询本地缓存的代码是是甚么模样。
Observable.create(new Observable.OnSubscribe<Object>() {
@Override
public void call(Subscriber<? super Object> subscriber) {
try {
List<Article> as = articleDao.queryBuilder()
.where(ArticleDao.Properties.CategoryId.eq(categoryId))
.orderDesc(ArticleDao.Properties.Id)
.offset((pageIndex - 1) * pageSize)
.limit(pageSize).list();
if (as == null || as.isEmpty()) {
subscriber.onNext(null);
}else{
subscriber.onNext(as);
}
}catch (Exception e){
subscriber.onError(e);
}
subscriber.onCompleted();
}
});
通过debug发现代码走的逻辑是
if (as == null || as.isEmpty()) {
subscriber.onNext(null);
}
发送的是空,为何还是没有走switchIfEmpty里的逻辑呢?肯定是我们用的姿式不对,先看看该该方法的说明:
/**
* Returns an Observable that emits the items emitted by the source Observable or the items of an alternate
* Observable if the source Observable is empty.
* <p/>
* <dl>
* <dt><b>Scheduler:</b></dt>
* <dd>{@code switchIfEmpty} does not operate by default on a particular {@link Scheduler}.</dd>
* </dl>
*
* @param alternate
* the alternate Observable to subscribe to if the source does not emit any items
* @return an Observable that emits the items emitted by the source Observable or the items of an
* alternate Observable if the source Observable is empty.
* @since 1.1.0
*/
public final Observable<T> switchIfEmpty(Observable<? extends T> alternate) {
return lift(new OperatorSwitchIfEmpty<T>(alternate));
}
重点关注对参数Observable<? extends T> alternate
的解释:
the alternate Observable to subscribe to if the source does not emit any items
意思是如果原来的Observable没有发射任何数据(emit any items),则使用alternate
代替原来的Observable。
好,再看看我们的代码逻辑:
if (as == null || as.isEmpty()) {
subscriber.onNext(null);
}
这段代码不是没有发射数据,而是发射了个空数据,也就是发射了null,所以这段代码其实不是没有发射任何数据,所以为何不走网络要求的逻辑。
知道缘由就好解决了,加上个过滤就能够解决问题了:
.filter(new Func1<RespArticlePaginate, Boolean>() {
@Override
public Boolean call(RespArticlePaginate respArticlePaginate) {
return respArticlePaginate != null;
}
})
1,通过switchIfEmpty可以做到1些逻辑判断,固然实现类型的判断本地缓存的,可以通过concat
结合takeFirst
操作符来实现,具体的可以看我之前的博客文章
2,上面通过Observable.create方式来包装数据查询,不是很优雅。下1篇博客介绍如何封装RxJava,使得我们的代码支持RxJava链式调用。