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 stringObservable = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                //发射数据
                e.onNext("我的");
                e.onNext("天啦");
            }
        });

        //创建观察者
        Observer myObserver = new Observer() {
            @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() ,如下图:

Android开发之深度项目设计探索(四)_第1张图片
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() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                e.onNext("你好啊");
                e.onNext("骑小猪看流星");
                e.onNext("改革春风吹满地");
                e.onNext("中国人民真争气");
                e.onNext("这个世界太疯狂");
            }
        }).subscribe(new Observer() {

             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  the value type
 */
public interface Consumer {
    /**
     * 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() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                e.onNext(" consumer : "+"改革春风吹满地");
                e.onNext(" consumer : "+"中国人民真争气");
                e.onNext(" consumer : "+"这个世界太疯狂");
            }
        }).subscribe(new Consumer() {
            @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 {
    @Override
    public void accept(String stirng) throws Exception {
        Log.i("info", "accept: "+stirng);
    }
}

  • subscribe( )传入该实现类:
    //传入 Consumer接口实现对象
    private void initSubConsumer() {
        Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter 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}.
     *  
Scheduler:
*
You specify which {@link Scheduler} this operator will use
* @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 subscribeOn(Scheduler scheduler) { ObjectHelper.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new ObservableSubscribeOn(this, scheduler)); }

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

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

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

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

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

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

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

        //创建观察者
        Observer myObserver = new Observer() {
            @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地址),文章请勿滥用,也希望大家尊重笔者的劳动成果

你可能感兴趣的:(Android开发之深度项目设计探索(四))