Android开发之深度项目设计探索(四)

本文是《Android开发之深度项目设计探索》系列的第四篇,主要介绍的是 基于最新Rxjava2类库的使用及部分操作符、实际项目中Rxjava2代码抽取封装进行说明分析,本系列历史文章:
《Android开发之深度项目设计探索(一)》
《Android开发之深度项目设计探索(二)》
《Android开发之深度项目设计探索(三)》

Rxjava2的设计理念

关于Rxjava2,已经有很多大佬分享了他们的使用和心得,我这里就简单描述下(注:要想用一篇文章写完Rxjava的前世今生和各种操作符的确很难,所以本篇文章主要是介绍基础概念以及一些对封装了Rxjava代码进行分析)。有很多朋友跟我说到,简单的Rxjava使用还是会,可是看到别人(公司内部)封装的Rxjava代码就晕晕乎乎的看不懂。由于Rxjava2是Rxjava1的拓展和延伸、内部主要是用到了 观察者模式(也有大佬为了更加具象化这种设计模式,称这种思想为管道流,管道流简单点理解就是上流指向下流的一种数据流向)。因此,在分析代码前还是要理一下观察者模式。

观察者模式简单理解就是:当被观察者发生变化,及时通知观察者(类似广播和EventBus)。所以,Rxjava2中的被观察者观察者在代码中表现的就分别是ObservableObserver ,注意:被观察者和观察者之间的关系是一对一或者一对多的关系,按照Java万物皆对象的设计理念,被观察者和观察者需要构建关系才来联合使用,这里的联合,在Rxjava中,就是 订阅 。毕竟,只有将被观察者和观察者两者联合起来才有意义(这里的意义,指的是数据的传递,试想如果被观察者和观察者两者之间无法构建订阅,想要传递的数据也就失去了方向),下面这个表格主要是为了加深记忆和方便日后使用

被观察者 观察者 订阅
Observable Observer subscribe
Rxjava2 的基本写法

毫无疑问,下面代码是大家最熟悉的Rxjava2写法:

    //大家最熟悉的Rxjava2写法
    private void initOriginRxjava() {

        //创建被观察者
        Observable<String> stringObservable = Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                //发射数据
                e.onNext("我的");
                e.onNext("天啦");
            }
        });

        //创建观察者
        Observer myObserver = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.i(TAG, "onSubscribe: ");
            }

            @Override
            public void onNext(String value) {
                Log.i(TAG, "onNext: "+value);
            }

            @Override
            public void onError(Throwable e) {
                Log.i(TAG, "onError: ");
            }

            @Override
            public void onComplete() {
                Log.i(TAG, "onComplete: ");
            }
        };

    }

相信这个写法大家非常熟悉(因为很多博客就是这样写的),上述代码定义了被观察者观察者也就是ObservableObserver。两个角色定义完毕,那么这两者的订阅在代码中该如何表示?订阅,在Rxjava2中也就是 subscribe() ,如下图:

Rxjava2-订阅

可以看到,subscribe是一个方法重载,该方法可以不用传参数,也可以传入Consumer(红色截图),也可以传入观察者 Observer( 黄色截图),由于上面的代码已经创建了观察者 Observer,且为了理清楚,直接传入一个Observer(直接传Observer也是最熟悉的方式):

订阅 - 1:传入Observer
        //订阅1:传递一个观察者
        stringObservable.subscribe(myObserver);

这种订阅,是我们最熟悉的写法,也是最基础的写法,Observable中的数据源最后传递到了Observer中的onNext(T value)中,

运行代码之后,日志显示如下:

com.example.ly.mystudy I/rxjava2: onSubscribe: 
com.example.ly.mystudy I/rxjava2: onNext: 我的
com.example.ly.mystudy I/rxjava2: onNext: 天啦

日志显示:首先调用了onSubscribe(),这里传递参数为Disposable,这个Disposable 相当于 RxJava 1.x 中的 Subscription, 主要功能是用于解除订阅,解除订阅的意思就是被观察者(Observable)还可以发射数据,但是观察者(Observer)无法收到收据。

订阅 - 1:传入Observer之链式写法

大家可能更加习惯是将Observable和Observer连起来(也就是链式写法),基础写法如下:

    //Rxjava2链式基础写法
    private void initOriginRxjava2() {
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext("你好啊");
                e.onNext("骑小猪看流星");
                e.onNext("改革春风吹满地");
                e.onNext("中国人民真争气");
                e.onNext("这个世界太疯狂");
            }
        }).subscribe(new Observer<String>() {

             Disposable mDisposable;

            @Override
            public void onSubscribe(Disposable d) {
                //解除订阅
                mDisposable = d;
                Log.i(TAG, "onSubscribe: ");
            }

            @Override
            public void onNext(String value) {
                if ("改革春风吹满地".equals(value)){
                    //满足条件则解除订阅
                    //解除订阅后 观察者则无法收到被观察者传来的数据源
                    mDisposable.dispose();
                }

                Log.i(TAG, "onNext: "+value);
            }

            @Override
            public void onError(Throwable e) {
                Log.i(TAG, "onError: ");
            }

            @Override
            public void onComplete() {
                Log.i(TAG, "onComplete: ");
            }
        });
    }

这种写法和上面的写法本质没什么大的区别,只是把 new Observer的过程放进了subscibe中,然后这里用到了 Disposable,上面也说到了 Disposable 主要功能是用于解除订阅,解除订阅后观察者(Observer)就无法收到收据。因此这个 Disposable 就可以根据实际开发条件来进行操作和限制了。

运行代码之后,日志显示如下:

com.example.ly.mystudy I/rxjava2: onSubscribe: 
com.example.ly.mystudy I/rxjava2: onNext: 你好啊
com.example.ly.mystudy I/rxjava2: onNext: 骑小猪看流星
com.example.ly.mystudy I/rxjava2: onNext: 改革春风吹满地
订阅 - 2:传入Consumer

上文说到subscribe()是一个方法重载,该方法也可以传入Consumer,那么这个Consumer是神马?点进源码看看

/**
 * A functional interface (callback) that accepts a single value.
 * @param <T> the value type
 */
public interface Consumer<T> {
    /**
     * Consume the given value.
     * @param t the value
     * @throws Exception on error
     */
    void accept(T t) throws Exception;
}

类注释翻译过来就是:这个接口的功能主要是用来接受唯一值。因此,这个值的类型就是声明的泛型,可以发现,accept方法内部抛了异常,throws Exception放在方法后边,意味着本方法不处理异常,交给被调用者处理(如果不希望异常层层往上抛,你就要用throws Exception) ,而且被调用者必须处理。

因此,Consumer简单理解它就是一个简易版的观察者(Observer)它主要是用来 接受单个值的,也就是通过accept(T t)来 接受 被观察者传过来的值

下面是 Consumer 基本的写法,也是最常见的写法:

    private void initOrginConsumer() {
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext(" consumer : "+"改革春风吹满地");
                e.onNext(" consumer : "+"中国人民真争气");
                e.onNext(" consumer : "+"这个世界太疯狂");
            }
        }).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.i(TAG, " accept: "+ s);
            }
        });
    }

运行代码之后,日志显示如下:

com.example.ly.mystudy I/rxjava2:  accept:  consumer : 改革春风吹满地
com.example.ly.mystudy I/rxjava2:  accept:  consumer : 中国人民真争气
com.example.ly.mystudy I/rxjava2:  accept:  consumer : 这个世界太疯狂

在实际or商业项目中,一般会把Consumer单独抽取出来,因为它是一个接口,实现该接口并在需要的位置传入具体的类也是可以的,

  • 实现Consumer接口:
public class TestConsumer implements Consumer<String> {
    @Override
    public void accept(String stirng) throws Exception {
        Log.i("info", "accept: "+stirng);
    }
}

  • subscribe( )传入该实现类:
    //传入 Consumer接口实现对象
    private void initSubConsumer() {
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext(" consumer : "+"改革春风吹满地");
                e.onNext(" consumer : "+"中国人民真争气");
                e.onNext(" consumer : "+"这个世界太疯狂");
            }
        }).subscribe(new TestConsumer());
    }

订阅 - 1 和 订阅 - 2的总结与区别:

订阅 - 1,使用了Observer,重载了4个方法,这4个方法的作用非常明显,可以解除订阅(拦截)、可以抛异常,可以接收数据,因此它是很基础和全面的;而订阅 - 2,个人的理解是,Consumer设计之初更加侧重的是 数据的传递(single value),简化代码提高效率,名副其实的 不管黑猫白猫,有数据就是好猫。

Rxjava2 的线程切换

所谓的线程切换,Rxjava2是基于Android诞生之初的理念来设计的,因为数据(请求、响应)的处理是耗时的,而Android又要在UI线程绘制界面来进行功能展示。如果在主线程执行耗时的操作,就可能会造成后面任务的阻塞、UI的卡顿等等一系列麻烦事情。为了解决这个问题,从早期的Handle,AsyncTask到现在的Rxjava2,都看见了线程切换的必然性。

言归正传,还是回到观察者模式,这种模式的本质是 被观察者 发生变化 才会去 通知观察者 ,因此,数据的产生实际上是源于被观察者(Observable), 产生的数据通过订阅指向给观察者,所以,观察者(Observer)最终会拿到数据。

综上,由于数据的产生(Observable)一般会通过后台接口来获取,这种耗时的操作基本上放在子线程来进行;由于观察者(Observer)最终会拿到数据 ,而Android拿到数据的目的大都是为了更新UI,因此,更新UI的Observer一般是在主线程。

Rxjava2中的线程切换,主要是

subscribeOn(Scheduler scheduler)
observeOn(Scheduler scheduler)

一些朋友说,看见这2个就分不清楚,简单的记忆办法是看见subscribe就想到订阅,看见observe就想起观察者,这样记忆就不会错了。

首先看下subscribeOn()的源码:

    /**
     * Asynchronously subscribes Observers to this ObservableSource on the specified {@link Scheduler}.
     *  <dt><b>Scheduler:</b></dt>
     *  <dd>You specify which {@link Scheduler} this operator will use</dd>
     * @param scheduler the {@link Scheduler} to perform subscription actions on
     * @return the source ObservableSource modified so that its subscriptions happen on the specified {@link Scheduler}
     */
    @SchedulerSupport(SchedulerSupport.CUSTOM)
    public final Observable<T> subscribeOn(Scheduler scheduler) {
        ObjectHelper.requireNonNull(scheduler, "scheduler is null");
        return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
    }

通过英文注释,这个方法的主要目的是:修改源ObservableSource,使其订阅发生在指定的调度程序。由于参数Scheduler的源码过多,这里就不进行说明,它主要是提供线程模型来进行线程切换,Scheduler常见的线程模型有以下四种:

  • Schedulers.io() :代表io操作的线程, 通常用于网络,读写文件等io密集型的操作;

  • Schedulers.computation() :代表CPU计算密集型的操作, 例如需要大量计算的操作;

  • Schedulers.newThread() :代表一个常规的新线程;

  • AndroidSchedulers.mainThread() :代表Android的主线程、UI线程

observeOn()是指定观察者接收数据的线程,一般指定为主线程即可,因此,在发射数据的时候;指定为子线程,接收数据为主线程,这样就可以进行合理的线程切换了。

    //Rxjava2 线程调度
    private void initRxjavaSchedulers() {
        //创建被观察者
        Observable<String> stringObservable = Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                Log.i(TAG, "Observable thread  == " + Thread.currentThread().getName());
                e.onNext("改革春风吹满地");
                e.onNext("中国人民真争气");
                e.onNext("这个世界太疯狂");
            }
        });

        //创建观察者
        Observer myObserver = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.i(TAG, "onSubscribe: ");
            }

            @Override
            public void onNext(String value) {
                Log.i(TAG, "Observer thread  == " + Thread.currentThread().getName());
                Log.i(TAG, "onNext: "+value);
            }

            @Override
            public void onError(Throwable e) {
                Log.i(TAG, "onError: ");
            }

            @Override
            public void onComplete() {
                Log.i(TAG, "onComplete: ");
            }
        };

        //订阅、线程切换
        stringObservable.subscribeOn(Schedulers.newThread()).
                observeOn(AndroidSchedulers. mainThread()).
                subscribe(myObserver);

    }

运行日志如下:

com.example.ly.mystudy I/rxjava2: onSubscribe: 
com.example.ly.mystudy I/rxjava2: Observable thread  == RxNewThreadScheduler-1
com.example.ly.mystudy I/rxjava2: Observer thread  == main
com.example.ly.mystudy I/rxjava2: onNext: 改革春风吹满地
com.example.ly.mystudy I/rxjava2: Observer thread  == main
com.example.ly.mystudy I/rxjava2: onNext: 中国人民真争气
com.example.ly.mystudy I/rxjava2: Observer thread  == main
com.example.ly.mystudy I/rxjava2: onNext: 这个世界太疯狂

本篇文章没有涉及到Rxjava2的操作符,仅介绍了Rxjava2的基础。基础的重要性不言而喻,打好基础就好比曾阿牛学会了九阳神功,他学其他的武功那就是易如反掌

(未完待续......)

如果这篇文章对您有开发or学习上的些许帮助,希望各位看官留下宝贵的star,谢谢。

Ps:著作权归作者所有,转载请注明作者, 商业转载请联系作者获得授权,非商业转载请注明出处(开头或结尾请添加转载出处,添加原文url地址),文章请勿滥用,也希望大家尊重笔者的劳动成果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容