Rxjava 从入门到开发,让 Rxjava 学习更加简单!!!

5,664 阅读17分钟
原文链接: www.jianshu.com

rxjava在业内越来越受欢迎,但是虽受欢迎却难理解,辛亏前面有大神们开路,并把心得体会记录流传后辈。比较出名的有抛物线,hi大头鬼等等,当时拜读大神们的文章,感受颇多,最深的体会是:虽得武林绝学,但无奈本人内力不足,学完秘籍还是觉得似懂非懂,得此招数,却无法理解精髓。但是经过一段时间的摸索,也慢慢有了一些对学习rxjava的心得体会,故记录下来充当自己的笔记,也希望可以给想学习rxjava的童鞋一些学习的思路。本篇文章主要分为两大部分:(1)如何学习rxjava(2)rxjava实战案例

如何学习rxjava,个人觉得可以从以下几个方面学习:

(1)使用rxjava的好处

随着项目功能增多,代码量会慢慢增加,复杂度也会加强,学习rxjava可以帮助我们:

通过以上两点可以规范团队的编码习惯提高效率,另外也方便我们定位问题,解决问题。在这里多说一句关于提高效率的问题,如何可以提高编码效率呢,个人觉得定好编码规范,另外写好代码架构非常重要,目前也出现了一些mvp,flux等等模式帮助我们定好项目规范。

(2)了解rxjava的基本元素

rxjava的基本三要素如下:

  • Observable:被观察者
  • Subscriber:观察者
  • OnSubscribe:一个接口类,是连接被观察者和观察者的桥梁,另外要说明的是onSubscribe是Observable的一个局部变量

上面这样官方的概念其实不容易理解😅,如何简单理解上面这“吉祥三宝”呢,本人是这么理解的,举个具体例子:造一辆小车,造小车的过程可以分为一道道连续的工序,简单来说就是:步骤一(造底盘),步骤二(加上轮子)等等,那么:
Observable:处理步骤一,步骤二等等操作的分别对应的“工厂一”,“工厂二”等等。
OnSubscribe:“工厂一”对应的“车间一”,“工厂二”对应的“车间二”
Subscriber:获取汽车成品的地方。

我们可以把一个任务拆分成为一个个依次执行的子任务,这种条理性也就是rxjava好用的地方。

(3)rxjava的操作符使用

在(1)(2)节中我大概介绍了rxjava的概念,让大家对rxjava有个感性的认识,但真正想学习rxjava还得从学习操作符开始,那么什么是操作符?简单来说:操作符就是Observable的各种操作,例如:创建,变换,过滤操作等等。在这里需要强调下的是,Observable通过操作符的操作之后会得到一个新的Observable,每创建一个操作符,简单来说就是创建了一个子任务,这个在后面源码分析会讲到,这里就不细讲了。

举个创建操作符Create的代码例子:代码结果很简单,依次输出“0,1,2”三个数,最后输出“hello rxjava execute complete”这句话。

     Observable.create(new Observable.OnSubscribe(){

        @Override
        public void call(Subscriber subscriber) {
            for(int i=0;i<3;i++){
                subscriber.onNext(i);
            }
            subscriber.onCompleted();
        }
    }).subscribe(new Subscriber() {
        @Override
        public void onCompleted() {
            Log.i(Log.TAG,"hello rxjava execute complete");

        }

        @Override
        public void onError(Throwable e) {

        }

        @Override
        public void onNext(Integer integer) {
            Log.i(Log.TAG,""+integer);

        }
    });

Observable 通过subscribe方法和Subscriber实现了订阅关系。

  Observable.subscribe(Subscriber subscriber)

在例子代码当中Observable通过create操作符创建了一个输出“0,1,2”这三个数的容器,也就是说创建了一个Observable对象,在这个容器中真正处理数据的是在Observable中的接口变量OnSubscribe的实现方法call(Subscriber subscriber) 里面,在call方法里面的subscriber其实就是前面说到订阅Observable的subscriber对象,最后的结果输出其实就是回调Subscriber的onNext(T),onCompleted()和onError(Throwable e)方法(错误处理)。

再举个创建操作符from的代码例子:代码结果很简单,输出“1,2,3”三个数

    Integer[] item={1,2,3};
    Observable.from(item).subscribe(new Action1() {
        @Override
        public void call(Integer integer) {
            Log.i(Log.TAG, "" + integer);
        }
    });

from需要传入是Iterable或者数组,然后在call方法中就会遍历Iterable或者数组输出里面的数据,仔细看看subscribe只传了一个Action1对象,没有发现subscriber对象,其实不然,跟踪方法进去,实际上还是走Observable被Subscriber订阅的套路,只不过这里的是ActionSubscriber。

public final Subscription subscribe(final Action1 onNext) {
    if (onNext == null) {
        throw new IllegalArgumentException("onNext can not be null");
    }

    Action1 onError = InternalObservableUtils.ERROR_NOT_IMPLEMENTED;
    Action0 onCompleted = Actions.empty();
    return subscribe(new ActionSubscriber(onNext, onError, onCompleted));
}

那么为啥这里传的是Action1呢,因为有时候我们只需要监听Subscriber的onNext(T)方法,不需要onCompleted()和onError(Throwable e)方法,因此可以直接传Action1对象即可。其实通过传递Action也可以实现那三个方法的。。😊

 Integer[] item={1,2,3};
    Observable.from(item).subscribe(new Action1() {
        @Override
        public void call(Integer integer) {
            Log.i(Log.TAG, "form " + integer);
        }
    }, new Action1() {
        @Override
        public void call(Throwable throwable) {
            Log.e(Log.TAG, "error: "+throwable.getMessage());
        }
    }, new Action0() {
        @Override
        public void call() {
            Log.i(Log.TAG,"from complete");
        }
    });

创建操作符还有Interval,Range等等,在此不一一说明。

下面再来个相对复杂的复杂的例子,也就是变换操作符,变换操作在rxjava非常重要,也是比较常用的操作符。
举个变换操作符map的具体例子:代码实现“0,1,2”三个数,如果是偶数就输出为true,否则为false。

 Observable.create(new Observable.OnSubscribe(){

        @Override
        public void call(Subscriber subscriber) {
            for(int i=0;i<3;i++){
                subscriber.onNext(i);
            }
            subscriber.onCompleted();
        }
    }).map(new Func1() {

        @Override
        public Boolean call(Integer integer) {
            return (integer%2)==0;
        }
    }).subscribe(new Action1() {
        @Override
        public void call(Boolean aBoolean) {
            Log.i("map",String.valueOf(aBoolean));
        }
    });

代码的输出结果为:


输出结果

从上面的例子看出,map的作用就是把Integer的数字转成了Boolean,这就是map的变换作用。是不是很强大😀,在例子中我们看到了一个Func1类,还有Action1,其实还有Actionx和Func1x他们的使用区别:Funcx处理中间变换过程,封装有返回值方法,Actionx表示输出结果,没有返回值,常用于代替onNext(T),onCompleted()或者onError(Throwable e)方法,这点上文也有提及。

更多操作符请参考接下来我这系列的文章,敬请期待吧。。。

既然map那么好用,我们不妨深入一点,Map是如何实现“变换”的呢?说明map的变换过程我准备分为以下几个步骤说明(ps:说明的代码为rxjava的1.1.6版本):

  1. 找到代码执行的起点
  2. 操作符map与“吉祥三宝”的关系
  3. 操作符map如何实现变换操作的过程

1.找到代码处理逻辑的起点

找到代码的触发点,简单来说就是找到代码在哪里开始执行的,举个创建符号Create的例子:

 Observable.create(new Observable.OnSubscribe(){

        @Override
        public void call(Subscriber subscriber) {
            for(int i=0;i<3;i++){
                subscriber.onNext(i);
            }
            subscriber.onCompleted();
        }
    }).subscribe(new Subscriber() {
        @Override
        public void onCompleted() {
            Log.i(Log.TAG,"hello rxjava execute complete");

        }

        @Override
        public void onError(Throwable e) {

        }

        @Override
        public void onNext(Integer integer) {
            Log.i(Log.TAG,""+integer);

        }
    });

我们都知道最开始是在call(Subscriber subscriber)调用一个for循环,但是这里有个疑问,call方法又是在哪里被调用的呢?我们不妨跟踪下代码,看到了Observable通过静态create方法创建了一个Observeble对象,Observable对象通过subscribe方法把Subscriber传了进去,如下:

  public final Subscription subscribe(Subscriber subscriber) {
    return Observable.subscribe(subscriber, this);
}

我们继续跟踪代码Observable.subscribe(subscriber, this)方法。。。

static  Subscription subscribe(Subscriber subscriber, Observable observable) {
        //省略其他代码,只保留核心说明部分
        // allow the hook to intercept and/or decorate
        hook.onSubscribeStart(observable, observable.onSubscribe).call(subscriber);
        return hook.onSubscribeReturn(subscriber);
}

我们暂时忽略hook,在这里我们就可以看到了call方法的调用,这里便是真正处理逻辑的起点,同时也建立了Observable和和Subscriber的订阅关系。例子大概过程是:代码调用了onSubscribe的call方法-->执行for循环-->通过call(Subscriber subscriber)传入的subscriber发送结果-->Subscriber的onNext等方法中订阅想要的结果。

2.操作符map与“吉祥三宝”的关系

根据上面案例,我们发现通过create操作符完成一次操作就涉及到”吉祥三宝“调用一次了,姑且用Observable0,Subscriber0,OnSubscribe0表示,那么通过create和map一起调用的话和”吉祥三宝“的联系又是怎样的呢,答案是map操作也涉及了"吉祥三宝",暂时可以用Observable1,Subscriber1,OnSubscribe1来表示。

Observable.create(new Observable.OnSubscribe(){

        @Override
        public void call(Subscriber subscriber) {
            for(int i=0;i<3;i++){
                subscriber.onNext(i);
            }
            subscriber.onCompleted();
        }
    }).map(new Func1() {

        @Override
        public Boolean call(Integer integer) {
            return (integer%2)==0;
        }
    }).subscribe(new Action1() {
        @Override
        public void call(Boolean aBoolean) {
            Log.i("map",String.valueOf(aBoolean));
        }
    });

在代码中很容易看到Observable调用map方法会返回一个新的Observable对象,也就是Observable1,我们再跟踪lift方法进去。。。。

  public final  Observable map(Func1 func) {
    return lift(new OperatorMap(func));
}

  public final  Observable lift(final Operator operator) {
    return new Observable(new OnSubscribeLift(onSubscribe, operator));
}

我们发现这里有个OnSubscribeLift对象,也就是OnSubscribe1,这里要注意的是在构建OnSubscribeLift对象的时候会把onSubscribe0传进去,也就是OnSubscribe1里面可以调用OnSubscribe0。现在Observable1和OnSubscribe1都找到了,还剩下Subscriber1,我们回头看map方法,找到OperatorMap这个类跟踪进去,发现里面有个内部类MapSubscriber,不用说了,MapSubscriber就是Subscriber1☺️。至此map涉及的Observable1(map方法返回),OnSubscribe1(OnSubscribeLift),Subscriber1(MapSubscriber)都已经全部找到。
至此可以简单总结为:Observable0操作通过操作符map产生了新的“吉祥三宝”,即:Observable1,OnSubscribe1,Subscriber1。

3.map操作的具体实现

通过分析map与“吉祥三宝”的关系,我们得到目前有两组“吉祥三宝”

  1. 通过操作符crate对应的Observable0,OnSubscribe0,Subscriber0。
  2. 通过操作符产生的Observable1,OnSubscribe1,Subscriber1。

在此要说明的是Observable0和Subscriber0,还有Observable1和Subscriber1还没有产生订阅关系。

分析map的变换操作,首先我们找到处理代码逻辑的起点,即:找到OnSubscribe对应的call方法,那么call方法对应的是OnSubscribe0还是OnSubscribe1呢?我们再看一次例子代码。。

  Observable.create(new Observable.OnSubscribe(){

        @Override
        public void call(Subscriber subscriber) {
            for(int i=0;i<3;i++){
                subscriber.onNext(i);
            }
            subscriber.onCompleted();
        }
    }).map(new Func1() {

        @Override
        public Boolean call(Integer integer) {
            return (integer%2)==0;
        }
    }).subscribe(new Action1() {
        @Override
        public void call(Boolean aBoolean) {
            Log.i("map",String.valueOf(aBoolean));
        }
    });

很明显是Observable0通过map方法产生的Observable1通过subscribe方法和Subscriber0发生订阅关系,那么第一步执行的代码便是OnSubscribe1的call方法,即:OnSubscribeLift的call方法,我们看下OnSubscribeLift具体对应的代码:

public final class OnSubscribeLift implements OnSubscribe {

static final RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook();

final OnSubscribe parent;

final Operator operator;

public OnSubscribeLift(OnSubscribe parent, Operator operator) {
    this.parent = parent;
    this.operator = operator;
}

@Override
public void call(Subscriber o) {
        //只保留关键代码
        Subscriber st = hook.onLift(operator).call(o);
        parent.call(st);

 }
}

在OnSubscribeLift的call方法里面,我们先定位hook.onLift(operator).call(o)这句代码,这句代码对应的是OperatorMap的call方法,具体如下:

public Subscriber call(final Subscriber o) {
    MapSubscriber parent = new MapSubscriber(o, transformer);
    o.add(parent);
    return parent;
}

明显看到该方法返回了Subscriber1(MapSubscriber)对象,st就是Subscriber1,再定位到parent.call(st)这句代码,这句代码通过OnSubscribe0的call方法实现了Observable0和Subscriber1(MapSubscriber)的订阅,继续顺着parent.call(st)方法,即:OnSubscribe0的call方法,我们终于回到了for循环。。😆

Observable.create(new Observable.OnSubscribe(){

    @Override
    public void call(Subscriber subscriber) {
        for(int i=0;i<3;i++){
            subscriber.onNext(i);
        }
        subscriber.onCompleted();
    }
})

在for循环中subscriber.onNext(i)的调用,其实这里的subscriber为subscriber1(MapSubscriber),我们再看看MapSubscriber的onNext方法。。。

  static final class MapSubscriber extends Subscriber {

    final Subscriber actual;

    final Func1 mapper;

    public MapSubscriber(Subscriber actual, Func1 mapper) {
        this.actual = actual;
        this.mapper = mapper;
    }

    //只保留需要说明的核心代码

    @Override
    public void onNext(T t) {
        R result;

        result = mapper.call(t);

        actual.onNext(result);
    }

}

从上面代码看到通过result = mapper.call(t)这句代码,实现了T到R的转换,在例子代码中就是Integer到Boolean的转换,这就是map变换的核心,转换后的结果通过actual.onNext(result)发送出去了,这个actual是什么东西呢,其实就是Subscriber0,具体可以看看MapSubscriber构造函数传入,这里就不一一说明了。。最后总结下整个过程。😊

  1. Observable1订阅了Subscriber0,即调用OnSubscribe1(OnSubscribeLift)的call方法,开始执行逻辑处理;
  2. 在OnSubscribe1的call方法中,OnSubscribe0调用call(T t)实现Observable0和Subscriber1(MapSubscriber)的订阅,由于调用了call方法,在例子中实际上就是调用了for循环;
  3. for循环中的调用了Subscriber1(MapSubscriber)的onNext方法发送数据;
  4. Subscriber1(MapSubscriber)的onNext方法中,通过result = mapper.call(t)实现了操作符map的数据转换;
  5. 最后在Subscriber1(MapSubscriber)在onNext方法中调用Subscriber0(ActionSubscriber)的onNext方法把结果回调到Subscriber0(ActionSubscriber)。

这里再用张图说下两组“吉祥三宝”的关系:


关系图

当然操作符可不仅这些,但是实现原理都可以参考上面map的分析步骤,找到起点,找到对应的“吉祥三宝”,再根据自己的理解,相信大家都会了解操作符的内部原理实现,更多的操作符可以操作我操作符系列文章。。

(3)rxjava的线程调度

除了操作符的使用,线程调度又是rxjava比较牛逼的功能,线程调度简单来说就是指定操作符操作在那个线程任务执行,通过Schedulers类来实现,先看个简单的例子:

  Observable.create(new Observable.OnSubscribe(){

        @Override
        public void call(Subscriber subscriber) {
            for(int i=0;i<3;i++){
                subscriber.onNext(i);
            }
            subscriber.onCompleted();
        }
    }).subscribeOn(Schedulers.io()).subscribe(new Action1() {
        @Override
        public void call(Integer integer) {
            Log.i("test",String.valueOf(integer));
        }
    });

例子中在操作符Create后面加上subscribeOn(Schedulers.io())方法就可以实现线程调度功能Schedulers.io()就是一个调度器,表示指定Create操作在子线程进行,subscribeOn的传入的调度器是可变的,具体调度器的种类和作用可以看以下图表:


调度器的种类

看到以上那么多调度器,那么rxjava是如何实现线程调度的呢,其实大家都很容易猜到,那就是利用线程池了。那么如何利用线程池实现的呢?其实还是回到“吉祥三宝”,subscribeOn方法返回的就是一个新的Observable,线程调度发生在OnSubscribe的call方法里面,这里就不一一展开了。
除了subscribeOn方法,还有其他方法可以实现线程调度吗?答案是有的,通过observeOn方法可以实现线程调度,还看是例子吧。。

   Observable.create(new Observable.OnSubscribe(){

        @Override
        public void call(Subscriber subscriber) {
            for(int i=0;i<3;i++){
                subscriber.onNext(i);
            }
            subscriber.onCompleted();
        }
    }).subscribeOn(Schedulers.io()).map(new Func1() {

        @Override
        public Boolean call(Integer integer) {
            return (integer%2)==0;
        }
    }).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1() {
        @Override
        public void call(Boolean aBoolean) {
            Log.i("map",String.valueOf(aBoolean));
        }
    });

我们定位到observeOn(AndroidSchedulers.mainThread())这句代码,先介绍下AndroidSchedulers.mainThread(),AndroidSchedulers.mainThread()其实也是个调度器,它和上文调度器不同的地方是,它是特有用于Android上面表示调度线程在主线程也就是ui线程上面工作,subscribeOn方法上面也可以传入AndroidSchedulers.mainThread()这个调度器。subscribeOn和observeOn方法都可以实现线程调度,那么他们有啥区别吗?

(1)subscribeOn方法在整个过程中只需要调用一次,即便调用多次也只有第一个subscribeOn方法有效;
(2) subscribeOn会对他前面和后面的操作有效,具体就是对create的call方法和map操作符的call方法产生作用;observeOn只会对后面的操作有效,具体就是把call(Boolean aBoolean)方法指定在ui线程工作;

具体看下抛物线的关于subscribeOn和observeOn混合使用时的说明图:


再引用下抛物线大神对上图的说明。。

图中共有 5 处含有对事件的操作。由图中可以看出,①和②两处受第一个 subscribeOn()影响,运行在红色线程;③和④处受第一个observeOn()的影响,运行在绿色线程;⑤处受第二个 onserveOn()影响,运行在紫色线程;而第二个 subscribeOn(),由于在通知过程中线程就被第一个 subscribeOn()截断,因此对整个流程并没有任何影响。这里也就回答了前面的问题:当使用了多个subscribeOn()的时候,只有第一个 subscribeOn()起作用。

最后再总结一下操作符和和线程调度。。把一个大功能通过不同的操作符依次分为一道道小工序,工序加工可以通过调度器指定在什么线程池执行,这个过程就是rxjava的作用,这种条理性就是rxjava的魅力所在。

上面说了那么多,还是用一些在Android项目中遇到的案例给大家演示下rxjava的作用吧。

(1)子线程耗时操作,主线程更新ui

项目中非常常见的功能,先贴实现代码:

Observable.create(new Observable.OnSubscribe() {
        @Override
        public void call(Subscriber subscriber) {
            Logger.i("执行耗时操作....");
            try {
                Thread.sleep(5000);
                subscriber.onNext("耗时操作完成...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1() {
        @Override
        public void call(String s) {
            Logger.i(s);
        }
    });

再贴输出结果:


实现功能主要通过subscribeOn和observeOn的组合使用,结果中的红框处就是对应分别的工作线程了。

(2)仿AsnycTack实现

大家应该都知道AsyncTask,它可以很容易实现(1),并且它可以更近一步,通过onPreExecute()方法在进行耗时任务之前可以让我进行一些准备操作,例如:显示个加载中的“菊花”转,最后onPostExecute方法中隐藏“菊花”,就类似下图一样。


以上功能,通过rxjava又是如何实现的呢?继续贴代码。。

@OnClick(R.id.btn2) void onButton2Click(){
    unBindSubscription();
    subscription= Observable.create(new Observable.OnSubscribe() {

        @Override
        public void call(Subscriber subscriber) {
            Logger.i("doInBackground()....");
            try {
                Thread.sleep(5000);
                subscriber.onNext("耗时操作完成...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }).subscribeOn(Schedulers.io()).doOnSubscribe(new Action0() {
        @Override
        public void call() {
            progressBar.setVisibility(View.VISIBLE);
            Logger.i("onPreExecute() ....");
        }
    }).subscribeOn(AndroidSchedulers.mainThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1() {
        @Override
        public void call(String s) {
            Logger.i("onPostExecute .... ".concat(s));
            progressBar.setVisibility(View.GONE);
        }
    });
}

doOnSubscribe方法就类似实现了AsyncTask的onPostExecute方法,但是需要注意的是:doOnSubscribe受到后面最近的subscribeOn方法影响运行所在的线程,默认会运行在subscribe()所在的线程。其他的代码也相对简单,大家可以自己看看。

(3)取消订阅

用rxjava在执行任务的过程当中,如果我们想停止的话,那应该怎么办呢?Subscriber订阅Observable的过程就是处理任务的过程,那么停止任务就是取消订阅操作。如何取消订阅?在仿AsyncTask的例子也提过,答案就是调用Subscription的unsubscribe方法,什么是Subscription?可以简单理解为执行任务的代号,看以下例子就可以很清楚了:

Subscription  subscription=Observable.create(new Observable.OnSubscribe(){

        @Override
        public void call(Subscriber subscriber) {
            for(int i=0;i<3;i++){
                subscriber.onNext(i);
            }
            subscriber.onCompleted();
        }
    }).subscribe(new Action1() {
        @Override
        public void call(Integer integer) {
        }
    });

很清楚看到整个任务的处理过程返回的就是一个Subscription😑。。在这里再抛出一个问题,为啥要取消订阅?有人说不是废话吗。。前面都已经说过了。。停止任务呀!!!是的,这是其中一个原因。。我根据自己的理解总结了下:

第一点:停止任务,这点也是我觉得比AsyncTask好用的地方,因为我们都知道doInbackgroud方法里面的事件停止不好控制,但是rxjava简单来说把doInbackgroud里面的逻辑分成几个小逻辑,如果取消订阅之后,后面的逻辑就不会再执行下去了,这也是rxjava比AsyncTask好用的地方。
第二点:防止内存泄露理解起来很简单,rxjava在处理逻辑中可能引用Activity的Context对象,要注意处理。
第三点:在某些情况之下我们想防止任务重复执行,那么我们通过取消订阅,停止上一个任务的执行,接着再执行新的任务。

(4)用rxbinding实现过滤输入

rxbinding :大神JakeWharton的又一杰作,用于防止用户误操作,例如:分词搜索输入的处理,连续点击的处理等等,可以看下图演示。。


再贴一下关键代码:

   RxTextView.textChanges(editText).debounce(500, TimeUnit.MILLISECONDS).observeOn(Schedulers.io()).map(new Func1>() {
        @Override
        public List call(CharSequence charSequence) {
            try {

                if(TextUtils.isEmpty(charSequence)){
                    return null;
                }
                List stringList =new ArrayList();
                String key=charSequence.toString();

                for (String num: baseDatas){
                    if(num.contains(key)){
                        stringList.add(num);
                    }
                }
                return stringList;
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    }).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1>() {
        @Override
        public void call(List list) {
            updateData(list);

        }
    }, new Action1() {
        @Override
        public void call(Throwable throwable) {
            Logger.e(Log.getStackTraceString(throwable));
        }
    });

代码比较简单就不一一多说了,这里需要注意的地方是:假如上图为页面一,在页面一打开一个页面二,然后关闭页面二返回页面一的时候,需要重新调用一次代码才能使得功能正常,所以代码一般在onStart方法执行

最后再总结一下:本文通过“学习rxjava的好处”引入,和大家分享学习rxjava,首先要了解它的三要素,接着就是操作符合和线程调度学习,最后通过一些项目案例实战和大家分享一些使用经验,但是不管怎样还是得多练习多打码,相信你终有收收获。由于本人水平有限,可能文中存在很多不足,希望大家多多谅解和提出,最后希望本篇文章可以给予那些想学习rxjava的小伙伴一些帮助吧😊。

附录:
文章demo

致谢:
给 Android 开发者的 RxJava 详解
Awesome-RxJava