RxJava(十一)defer 操作符实现代码支持链式调用

15 篇文章 42 订阅

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 线程的自由切换


一、前言

现在越来越多 Android 开发者使用到 RxJava,在 Android 使用 RxJava 主要有如下好处:

  • 轻松切换线程。

  • 轻松解决回调的嵌套问题。现在的 App 业务逻辑越来越复杂,多的时候 3,4 层回调嵌套,使得代码可维护性变得很差。RxJava 链式调用使得这些调用变得扁平化。

随着 RxJava 的流行,越来越多的开源项目开始支持 RxJava,像 Retrofit、GreenDao 等。这些开源项目支持 RxJava 使得我们解决复杂业务变得非常方便。

但是这些还不够,有的时候我们自己的封装的业务也需要支持 RxJava,举个例子:查询数据、处理本地文件等操作,总而言之就是一些耗时任务。而且还要处理这些操作的成功、失败、线程切换等操作。如果还是像以前那样做,那就太不优雅了。

二、下面就来探讨下如何使得代码支持 RxJava 风格

遇到这种问题,在我脑海里浮现的第一种方式就是通过 Observable 的 create 操作符。因为在里面我们可以控制数据的发射。就像上一篇文章那样《RxJava switchIfEmpty 操作符实现 Android 检查本地缓存逻辑判断》

如下代码片段:

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();
            }
        });

这样确实没有没有问题。但是我们要封装下, 每个方法都这样写维护性和扩展比较差(例如有天我想换种方式来实现而不是 create,如果通过方法封装一下,修改就变得容易多了)
如何封装呢?通过分析知道,大部分代码是相同的,只是我们的业务不一样。那么通过模板方法解决吧。业务方法通过接口回调的方式传递进来,因为我们不知道调用者是什么业务。

回调接口如下(T表示我们业务数据):

    public interface MyCallable<T> {
        T call();
    }

下面是模板代码:

    protected <R> Observable<R> createObservable(final MyCallable<R> callable) {
        return Observable.create(new Observable.OnSubscribe<R>() {
            @Override
            public void call(Subscriber<? super R> subscriber) {
                try {
                    R result = callable.call();
                    subscriber.onNext(result);
                } catch (Exception e) {
                    subscriber.onError(e);
                }
                subscriber.onCompleted();
            }
        });
    }

使用就非常简单了调用createObservable方法,实现MyCallable接口即可,然后就是跟使用RxJava一样处理逻辑。

三、分析 GreenDao 是如何支持 RxJava 风格的

看过 GreenDao 源码的人知道,它也是通过这种方式支持 RxJava 的(下面看看他是怎么做的):

    /**
     * Rx version of {@link AbstractDao#loadAll()} returning an Observable.
     */
    @Experimental
    public Observable<T> load(final K key) {
        return wrap(new Callable<T>() {
            @Override
            public T call() throws Exception {
                return dao.load(key);
            }
        });
    }

最终的实现也是通过 dao.load(key) 同步方法来实现的,关键是 wrap 方法了:

    protected <R> Observable<R> wrap(Callable<R> callable) {
        return wrap(RxUtils.fromCallable(callable));
    }

    //通过这个方法再包装了一层(就是默认设置执行的线程)
    protected <R> Observable<R> wrap(Observable<R> observable) {
        if (scheduler != null) {
            return observable.subscribeOn(scheduler);
        } else {
            return observable;
        }
    }

通过代码可以看到默认执行的线程是 IO 线程:

    /**
     * The returned RxDao is a special DAO that let's you interact with Rx Observables using RX's IO scheduler for
     * subscribeOn.
     *
     * @see #rxPlain()
     */
    @Experimental
    public RxDao<T, K> rx() {
        if (rxDao == null) {
            rxDao = new RxDao<>(this, Schedulers.io());
        }
        return rxDao;
    }

所以使用 GreenDao 不用指定它在 IO 执行,因为框架已经帮我们设置了。

然后就是RxUtils.fromCallable(callable)方法了:

class RxUtils {
    /** As of RxJava 1.1.7, Observable.fromCallable is still @Beta, so just in case... */
    @Internal
    static <T> Observable<T> fromCallable(final Callable<T> callable) {
        return Observable.defer(new Func0<Observable<T>>() {

            @Override
            public Observable<T> call() {
                T result;
                try {
                    result = callable.call();
                } catch (Exception e) {
                    return Observable.error(e);
                }
                return Observable.just(result);
            }
        });
    }
}

上面的注释说通过 Observable.fromCallable 也可以实现这样的逻辑,也就是说代替Observable.defer() 方法。
最后 GreenDao 是通过 defer 操作符来实现 RX 风格的。

四、defer和 create 操作符有什么异同点?

通过分析 GreenDao 源码得知,他是通过 defer 来做的,我们是通过 create 操作符来做的。那两者有什么不同?

我们对 defer 操作符比较陌生,先看看它的源码:

    public static <T> Observable<T> defer(Func0<Observable<T>> observableFactory) {
        return create(new OnSubscribeDefer<T>(observableFactory));
    }

说白了就是调用了create(OnSubscribe<T> f) 方法:

    public static <T> Observable<T> create(OnSubscribe<T> f) {
        return new Observable<T>(hook.onCreate(f));
    }

其实我们上面的 create 操作也是调用过来这个方法。只是 defer 操作符传递的 OnSubscribe 是 OnSubscribeDefer,那我们来看看这是什么鬼?

public final class OnSubscribeDefer<T> implements OnSubscribe<T> {
    final Func0<? extends Observable<? extends T>> observableFactory;

    public OnSubscribeDefer(Func0<? extends Observable<? extends T>> observableFactory) {
        this.observableFactory = observableFactory;
    }

    @Override
    public void call(final Subscriber<? super T> s) {
        Observable<? extends T> o;
        try {
            o = observableFactory.call();
        } catch (Throwable t) {
            Exceptions.throwOrReport(t, s);
            return;
        }
        o.unsafeSubscribe(Subscribers.wrap(s));
    }
    
}

OnSubscribeDefer 也是继承自 OnSubscribe,那么他的 call 方法肯定也是在订阅的时候被调用(就是说订阅的时候才创建这个 observable,并且每次订阅都会创建一个新的 observable)。
为什么 GreenDao 没有使用 create 那种方式精确控制数据的发射?现在 RxJava2.0 对create 操作符做出了一些限制,不能随随便便 create 了,这样出现一些问题。具体的RxJava2.0 的改动可以看看
他的 Github 说明 What’s-different-in-2.0

五、参考资料:

本文的例子放在 Github 上 https://github.com/chiclaim/AndroidAll


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

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

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

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
引用\[2\]:上面我们介绍了了关于RxJava的基本的概念和使用方式,在下面的文章中我们会按照以上定义的顺序从API的角度来讲解以下RxJava各个模块的使用方法。引用\[3\]:retry使用了一种错误重试机制,它可以在出现错误的时候进行重试,我们可以通过参数指定重试机制的条件。以下面的程序为例,这里我们设置了当出现错误的时候会进行2次重试,因此,第一次的时候出现错误会调用onNext,重试2次又会调用2次onNext,第二次重试的时候因为重试又出现了错误,因此此时会触发onError方法。也就是说,下面这段代码会触发onNext3次,触发onError()1次。 问题: rxjava2 defer是什么意思? 回答: 在RxJava2中,defer是一个非常有用的操作符。它允许我们在每次订阅时创建一个新的Observable,以便能够动态地生成Observable。defer操作符接受一个Observable的工厂函数作为参数,当有观察者订阅时,它会调用这个工厂函数创建一个新的Observable。这样,每次订阅时都会得到一个全新的Observable实例,可以确保每个订阅者都能够获取到最新的数据。defer操作符的使用场景包括延迟创建Observable、动态地生成Observable等。 #### 引用[.reference_title] - *1* *2* *3* [RxJava 系列-1:一篇的比较全面的 RxJava2 方法总结](https://blog.csdn.net/weixin_34411563/article/details/87985915)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Chiclaim

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

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

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

打赏作者

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

抵扣说明:

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

余额充值