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() {            @Override            public void call(Subscriber<? super Object> subscriber) {                try {                    List 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.     * 

*

*
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 思维导图:

更多相关文章

  1. Android开发 华为手机读取相册闪退问题
  2. android greenDao SQLite数据库操作工具类
  3. Android游戏应用内悬浮球(不需要申请权限)
  4. Android(安卓)SQLiteDatabase使用总结
  5. Android中的位运算的例子(一)
  6. ContentProvider总结
  7. Fragment封装切换
  8. 编写高效Android代码
  9. android email 邮件转发 附件不能转发问题的解决

随机推荐

  1. android 开发者选项
  2. 001——Binder 机制详解—Binder IPC 程
  3. [置顶] 我的Android进阶之旅------>Andro
  4. native.js获取手机硬件基本信息实例代码a
  5. 将ffmpeg移植到Android
  6. @suppresslint 标注忽略指定的警告(Handl
  7. android 开发中遇到未知问题的记录
  8. udacity android 学习笔记: lesson 4 par
  9. Android(安卓)通过Base64上传图片到服务
  10. linux系统灵活运用灯[android课程3]