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

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

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

一、前言

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

1,轻松切换线程。以前我们切换线程主要使用Handler等手段来做。

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

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

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

如果还是想以前那样做,那就太low。

二、下面就来探讨下如何使得代码支持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

五、参考资料:

pitfalls-of-operator-implementations

subscribe vs unsafeSubscribe

What’s-different-in-2.0

RxJava(十一)defer操作符实现代码支持链式调用的更多相关文章

  1. 自定义php-mysqli工具增强类,支持链式调用

    <?php /*数据库访问类,支持链式访问 *function table($table):表名 *function where($where):条件 *function field(...$f ...

  2. js链式调用

    我们都很熟悉jQuery了,只能jQuery中一种非常牛逼的写法叫链式操作 * $('#div').css('background','#ccc').removeClass('box').stop() ...

  3. C#中扩展StringBuilder支持链式方法

    本篇体验扩展StringBuilder使之支持链式方法. 这里有一个根据键值集合生成select元素的方法. private static string BuilderSelectBox(IDicti ...

  4. jQuery支持链式编程,一句话实现左侧table页+常用筛选器总结

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  5. JavaScript运动_封装模板(支持链式运动、完美运动)

    最近自学到了JS运动部分,自己整理了一些js模板,望采纳. 1.支持链式运动的模板: 先解释一下函数中的几个参数含义: 1)obj: 要操作的对象 2)target: 属性要到达的目标值 3)attr ...

  6. 史上最简单的手写Promise,仅17行代码即可实现Promise链式调用

    Promise的使用相比大家已经孰能生巧了,我这里就不赘述了 先说说我写的Promise的问题吧,无法实现宏任务和微任务里的正确执行(也就是在Promise里面写setTimeout,setInter ...

  7. 如何写 JS 的链式调用 ---》JS 设计模式《----方法的链式调用

    1.以$ 函数为例.通常返回一个HTML元素或一个元素集合. 代码如下: function $(){ var elements = []; ;i<arguments.length;i++){ v ...

  8. JavaScript设计模式——方法的链式调用

    方法的链式调用: (function() { //私有类 function _$ (els) { this.elements = []; for(var i = 0, len = els.length ...

  9. 浅析 JavaScript 链式调用

    对$函数你已经很熟悉了.它通常返回一个html元素或一个html元素的集合,如下: function$(){ var elements = []; for(vari=0,len=arguments.l ...

随机推荐

  1. golang如何使用channel控制goroutine退出

    最经典的处理方式: 在启动goroutine的时候,传递一个额外的chan型参数,用来接收退出信号,代码如下 func worker(name string, stopchan chan struct ...

  2. mysql索引类型和索引方法

    索引类型 mysql索引类型normal,unique,full text的区别是什么? normal:表示普通索引 unique:表示唯一的,不允许重复的索引,如果该字段信息保证不会重复例如身份证号 ...

  3. [LeetCode] Maximum Length of Pair Chain 链对的最大长度

    You are given n pairs of numbers. In every pair, the first number is always smaller than the second ...

  4. IOS开发-UIDynamic(物理仿真)简单使用

    UIDynamic是从IOS7开始引入的一种新技术,隶属于UIKit框架,我们可以认为是一种物理引擎能模拟和仿真现实生活中的物理现象,比如重力,弹性碰撞等. 可以让开发人员远离物理公式的情况下,实现一 ...

  5. [BZOJ 2169]连边

    Description 有N个点(编号1到N)组成的无向图,已经为你连了M条边.请你再连K条边,使得所有的点的度数都是偶数.求有多少种连的方法.要求你连的K条边中不能有重边,但和已经连好的边可以重.不 ...

  6. [SHOI2016]黑暗前的幻想乡

    Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪 ...

  7. POJ 1486二分图的必要边

    题意:有n个大小不等透明的幻灯片(只有轮廓和上面的数字可见)A.B.C.D.E…按顺序叠放在一起,现在知道每个幻灯片大小,由于幻灯片是透明的,所以能看到幻灯片上的数字(给出了每个数字的坐标,但不知道这 ...

  8. [BZOJ]4810: [Ynoi2017]由乃的玉米田

    Time Limit: 30 Sec  Memory Limit: 256 MB Description 由乃在自己的农田边散步,她突然发现田里的一排玉米非常的不美.这排玉米一共有N株,它们的高度参差 ...

  9. ●BZOJ 3996 [TJOI2015]线性代数

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3996 题解: 好题啊.(不太熟悉矩阵相关,所以按某些博主的模型转换来理解的)首先,那个式子可 ...

  10. [BZOJ]3926 诸神眷顾的幻想乡(ZJOI2015)

    听说大佬们都会后缀自动机. 小C看完SAM,想找个裸题练习一下模板.听说这题还是陈老师出的?(羊毛出在羊身上) Description  幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生 ...