RxJava(十)switchIfEmpty 操作符实现 Android 检查本地缓存逻辑判断

15 篇文章 43 订阅

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.<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 链式调用。


如果你觉得本文帮助到你,给我个关注和赞呗!

另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图

如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图:
超详细的Android技术栈

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiclaim

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值