RxJava 系列文章目录导读:
一、RxJava create 操作符的用法和源码分析
二、RxJava map 操作符用法详解
三、RxJava flatMap 操作符用法详解
四、RxJava concatMap 操作符用法详解
五、RxJava onErrorResumeNext 操作符实现 app 与服务器间 token 机制
六、RxJava retryWhen 操作符实现错误重试机制
七、RxJava 使用 debounce 操作符优化 app 搜索功能
八、RxJava concat 操作处理多数据源
九、RxJava zip 操作符在 Android 中的实际使用场景
十、RxJava switchIfEmpty 操作符实现 Android 检查本地缓存逻辑判断
十一、RxJava defer 操作符实现代码支持链式调用
十二、combineLatest 操作符的高级使用
十三、RxJava 导致 Fragment Activity 内存泄漏问题
十四、interval、takeWhile 操作符实现获取验证码功能
十五、RxJava 线程的自由切换
switchIfEmpty(Observable emptyObservable) 操作符从字面意思上就很好理解,就是当为空的时候跳转到 emptyObservable。
那么如何理解当为空的时候
. 下面将会使用实际案例解释这个 switchIfEmpty
的使用方法。
假如我们的 App 里有加载文章列表功能,要求加载的逻辑如下:加载文章的的时候,先从本地加载,如果本地存在就是用本地的数据,如果不存在从网络获取。
下面是业务代码:
//从本地数据获取文章列表
getArticlesObservable(pageIndex, pageSize, categoryId)
//本地不存在,请求api
.switchIfEmpty(articleApi.getArticlesByCategoryId(pageIndex + "", pageSize + "", categoryId + "")
.compose(this.handlerResult())
.flatMap(new Func1>() {
@Override
public Observable 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 对成功、失败的数据处理,然后统一把数据推给上一层,就不用每个地方都写下面相同的模板代码了:
observable.subscribe(new Action1() {
@Override
public void call(RespArticlePaginate respArticlePaginate) {
//success data
}
}, new Action1() {
@Override
public void call(Throwable throwable) {
// error data
}
})
那么 createSubscriber
是如何实现的,先看 subscribe 方法源码 如下:
public final Subscription subscribe(final Action1 super T> onNext, final Action1 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(onNext, onError, onCompleted));
}
很简单,他是直接 new 了一个 ActionSubscriber,然后把我们以前在代码里写的各个回调 (onNext、onError、onComplete) 当做参数传递进去。那么我们的 createSubscriber
也可以模拟它的实现:
/**
* 处理结果(分发结果) 封装
*
* @param id 区分业务类型
*/
protected ActionSubscriber createSubscriber(final int id) {
//因为我们只关心onNext和onError
Action0 onCompleted = Actions.empty();
return new ActionSubscriber(new Action1() {
@Override
public void call(T t) {
pushSuccessData(id, t);
}
}, new Action1() {
@Override
public void call(Throwable throwable) {
pushThrowable(id, throwable);
}
}, onCompleted);
}
好了,言归正传,回到我们上面提到的需求。根据需求我们来分析下代码:
getArticlesObservable
方法用来从本地获取文章列表,articleApi.getArticlesByCategoryId
方法是用来当本地不存在的时候从网络获取。似乎这些代码可以实现了我们上面提到的需求了。而且很简洁。
实践是检验真理的唯一标准,我们先运行下看看(本地环境是数据库没有文章列表)。
运行后,发现界面并没有展示数据,通过 debug 发现,代码执行了检测本地缓存的逻辑,且本地找不到符合逻辑的数据,也就是说从本地找到的结果为空。但是没有按照我们预想的是执行网络请求。
先来看看查询本地缓存的代码是是什么样子。
Observable.create(new Observable.OnSubscribe
通过 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.
*
*
* - Scheduler:
* - {@code switchIfEmpty} does not operate by default on a particular {@link Scheduler}.
*
*
* @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 switchIfEmpty(Observable extends T> alternate) {
return lift(new OperatorSwitchIfEmpty(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() {
@Override
public Boolean call(RespArticlePaginate respArticlePaginate) {
return respArticlePaginate != null;
}
})
1,通过 switchIfEmpty 可以做到一些逻辑判断,当然实现类型的判断本地缓存的,可以通过concat
结合takeFirst
操作符来实现,具体的可以看我以前的 博客文章
2,上面通过 Observable.create 方式来包装数据查询,不是很优雅。下一篇博客介绍如何封装 RxJava,使得我们的代码支持 RxJava 链式调用。
另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图。
如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图: