欢迎转载,转载请标明出处:

http://blog.csdn.net/johnny901114/article/details/52585912

本文出自:【余志强的博客】

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对成功、失败的数据处理,然后统一把数据推给上一层,就不用每个地方都写下面相同的模板代码了:


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了一个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方法是用来当本地不存在的时候从网络获取。似乎这些代码可以实现了我们上面提到的需求了。而且很简洁。

实践是检验真理的唯一标准,我们先运行下看看(本地环境是数据库没有文章列表)。

运行后,发现界面并没有展示数据,通过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可以做到一些逻辑判断,当然实现类型的判断本地缓存的,可以通过concat结合takeFirst操作符来实现,具体的可以看我以前的博客文章

2,上面通过Observable.create方式来包装数据查询,不是很优雅。下一篇博客介绍如何封装RxJava,使得我们的代码支持RxJava链式调用。

RxJava(十)switchIfEmpty操作符实现Android检查本地缓存逻辑判断的更多相关文章

  1. Android ImageLoader 本地缓存

    Android ImageLoader 本地缓存 本地缓存                                                                        ...

  2. Android -- ImageLoader本地缓存

    传送门 <Android -- ImageLoader简析>  http://www.cnblogs.com/yydcdut/p/4008097.html 本地缓存 在缓存文件时对文件名称 ...

  3. Android 清除本地缓存

    主要功能:清除内.外缓存,清除数据库,清除Sharepreference,清除files和清除自定义目录 public class DataCleanManager { //清除本应用内部缓存(/da ...

  4. 深入浅出RxJava(二:操作符)

    看完这篇blog,我相信你肯定想立即在你的项目中使用RxJava了,这篇blog将介绍许多RxJava中的操作符,RxJava的强大性就来自于它所定义的操作符. 首先先看一个例子: 准备工作 假设我有 ...

  5. RxJava学习笔记(操作符)

    前言 上一篇文章介绍了RxJava的基础知识和简单实现,篇幅已经比较多了,所以把操作符(Operators)相关的内容放在这一篇.有了上一篇文章的基础,相信会比较容易理解操作符相关的内容了. 操作符( ...

  6. Android清除本地数据缓存代码案例

    Android清除本地数据缓存代码案例 直接上代码: /*  * 文 件 名:  DataCleanManager.java  * 描    述:  主要功能有清除内/外缓存,清除数据库,清除shar ...

  7. Android远程图片获取和本地缓存

    对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,对 应用来说,如果处理不好这个问题,那会让用户很崩溃,不知不觉手机流量就用完了,等用户发现是你的应用 ...

  8. spring boot / cloud (十八) 使用docker快速搭建本地环境

    spring boot / cloud (十八) 使用docker快速搭建本地环境 在平时的开发中工作中,环境的搭建其实一直都是一个很麻烦的事情 特别是现在,系统越来越复杂,所需要连接的一些中间件也越 ...

  9. Android中本地广播的实现

    其实Android的本地广播并没有什么好讲的,他就是用了一个localbroadcastmanager类来sendbroadcast,以及注册和注销广播,没有什么特点,其中实例该类的时候用了getin ...

随机推荐

  1. 关于字数太多直接变成省略号的方法css

    文字超出限制的宽度自动隐藏,并且变为省略号 这是之前写的,现在要做一个两行的 于是万能找百度,居然真的有这个方法: 于是,我就变成了搬运工:○( ^皿^)っHiahiahia- http://blog ...

  2. ios开发-程序压后台后,悄悄的抓取数据~~

    我们使用某个app的时候,当我们将程序压到后台之后,我们希望它还能从服务器抓取一些数据,类似微博,微信,qq这些程序压后台 之后,我们依然能看到icon上显示未读数量.但是ios系统是伪多任务操作系统 ...

  3. Shell编程-项目部署(一)

    由于实际工作中经常用到需要部署项目,比较麻烦,今天记录下如何利用shell脚本完成自动部署的工作,毕竟不是专业的运维出身,写的不好,还请勿喷_^o^_ 今天以部署Django项目为演示,进行部署项目, ...

  4. [BZOJ 4361]isn

    Description 题库链接 给出一个长度为 \(n\) 的序列 \(A\) .如果序列 \(A\) 不是非降的,你必须从中删去一个数,这一操作,直到 \(A\) 非降为止.求有多少种不同的操作方 ...

  5. Go学习——go+channel实战(转)

    转载:http://studygolang.com/articles/2423 背景 在最近开发的项目中,后端需要编写许多提供HTTP接口的API,另外技术选型相对宽松,因此选择Golang + Be ...

  6. SPOJ NSUBSTR

    You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as ...

  7. [WC2008]游览计划

    [题目描述] 从未来过绍兴的小 D 有幸参加了Winter Camp 2008,他被这座历史名城的秀丽风景所吸引,强烈要求游览绍兴及其周边的所有景点. 主办者将绍兴划分为 N 行M 列(N×M)个方块 ...

  8. VS2012代码对齐快捷键

    1.选中想要对齐的代码 2.全选代码后按住Ctrl+K,Ctrl+F键,就可以了

  9. Zend引擎探索 之 PHP中前置递增不返回左值

    首先来讲,一般我们对"左值"的理解就是可以出现在赋值运算符的左侧的标识符,也就是可以被赋值.这样讲也许并不十分确切,在不同的语言中对左值的定义也不尽相同.在这里我们讨论前置递增(和 ...

  10. Linux学习之CentOS(十九)------linux 下压缩与解压之 tar、gzip、bzip2、zip、rar

    将文件存储到归档文件中或者从归档文件中获取原始文件,以及为文件创建归档文件 tar [option] [modifiers] [file-list] 参数 file-list是tar进行归档和提取的文 ...