【android】RxJava1原理解析

写在前面

读了大神的博客,为了学习,跪着看完再整理了出来,以便加深印象。

RxJava 是什么,能解决什么问题

  • github 官方介绍

RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences.

一个运行在 JVM 上的库,通过可观测的序列来组成异步的,基于事件的程序。

  • 解决问题

    让复杂的程序逻辑回归简单、清晰。​

API介绍和原理简析

1、观察者模式

  • RxJava 的四个基本概念
    • Observable 可观察者、被观察者
    • Observer 观察者、订阅者
    • subscribe 订阅
    • Event 事件

Observable 和 Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。

与传统观察者模式不同,RxJAva的事件回调方法除了普通事件 onNext()(相当于onClick()/onEvent())之外,还定义了两个特殊的事件:onCompleted()onError()

  • onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。

  • onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。

  • 在一个正确运行的事件序列中, onCompleted()onError() 有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted()onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。

这里写图片描述

2、基本实现

基于以上的概念,RxJava的基本实现主要有三点:

(1)创建Observer

Observer<String> observer = new Observer<String>() {
        @Override
        public void onCompleted() {
            Log.d(TAG,"Completed!");
        }

        @Override
        public void onError(Throwable e) {
            Log.d(TAG,"Error!");
        }

        @Override
        public void onNext(String s) {
            Log.d(TAG,"Item:" + s);
        }
    };

除了Observer接口外,RxJava还内置了一个实现了Observer的抽象类:Subscriber。Subscriber堆Observer接口进行了一些扩展,但是它们的基本使用方式是完全一样的。不仅基本使用方式一样,实质上,在RxJava的subscribe过程中,Observer也总是会先被转换成一个Subscriber再使用。它们区别对于使用者来说主要有两点:

  1. onStart():这是 Subscriber增加的方法。它会在subscribe刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。需要注意的是:如果准备工作的线程有要求,onStart()就不适用了,因为它总是在subscribe所发生的线程被调用,而不能指定线程。要再指定的线程来你做准备工作,可以适用doOnSubscribe()方法。
  2. unsubscribe():这是Subscriber所实现的另一个接口Subscription的方法,用于取消订阅。在这个方法被调用后,Subscriber将不再接收事件。避免内存泄漏。

(2)创建Observable

RxJava适用create()方法来创建一个Observable,并为它定义事件触发规则:

Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
            subscriber.onNext("I'm a");
            subscriber.onNext("I'm b");
            subscriber.onNext("I'm c");
            subscriber.onCompleted();
        }
    });

可以看到,这里传入了一个OnSubscribe对象作为参数。OnSubscribe会被存储在返回的Observable对象中,它的作用相当于一个计划表,当Observable被订阅的时候,OnSubscribe的call()方法会自动被调用。这样,由被观察者调用了观察者的回掉方法,就能实现由被观察者向观察者的事件传递,即观察者模式。

create()方法是RxJava最基本的创造事件序列的方法。基于这个方法,RxJava还提供了以下方法用来快捷创建事件队列,例如:

  • just(T...):将传入的参数一次发送出来。

    Observable observable = Observable.just("I'm a","I'm b","I'm c");
    //会依次调用 
    // onNext("I'm a");
    // onNext("I'm ");
    // onNext("I'm c");
    // onCompleted();
  • from(T[])/from(Iterable<? extends T>):将传入的数组或者Iterable拆分成具体对象后,依次发送出来。

    String[] words = {"I'm a","I'm b","I'm c"};
     Observable observable = Observable.from(words);

(3)Subscribe(订阅)

创建了观察者和被观察者之后,再用subscribe()方法将它们联合起来,整条链子就可以工作了。

observable.subscribe(observer);

Observable.subscribe(Subscriber)的内部是显示这样的(仅核心代码):

// 注意:这不是 subscribe() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
// 如果需要看源码,可以去 RxJava 的 GitHub 仓库下载。
public Subscription subscribe(Subscriber subscriber) {
    subscriber.onStart();
    onSubscribe.call(subscriber);//在RxJava中,Observable并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当subscribe()方法执行的时候。
    return subscriber;//作为Subscription返回。这是为了方便unsubscribe();
}

除了subscribe(Observer)subscribe(Subscriber),subscribe()还支持不完整定义的回掉,RxJava会自动根据定义创建出Subscriber。形式如下:

//Action1也是一个接口,,它同样只有一个方法 call(T param),这个方法也无返回值,但有一个参数;与 Action0 同理,由于 onNext(T obj) 和 onError(Throwable error) 也是单参数无返回值的,因此 Action1 可以将 onNext(obj) 和 onError(error) 打包起来传入 subscribe() 以实现不完整定义的回调。
Action1<String> onNextAction = new Action1<String>() {
    @Override
    public void call(String s) {
      Log.d(TAG,s);
    }
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
    @Override
    public void call(Throwable throwable) {

    }
};
//Action0使RxJava的一个接口,它只有一个方法call(),这个方法是无参无返回值的;由于 onCompleted() 方法也是无参无返回值的,因此 Action0 可以被当成一个包装对象,将 onCompleted() 的内容打包起来将自己作为一个参数传入 subscribe() 以实现不完整定义的回调。这样其实也可以看做将 onCompleted() 方法作为参数传进了 subscribe(),相当于其他某些语言中的『闭包』。
Action0 onCompletedAction = new Action0() {
    @Override
    public void call() {
      Log.d(TAG,"completed");
    }
};
//自动创建 Subscriber,并使用 onNextAction 来定义 onNext() 
observable.subscribe(onNextAction);
//自动创建 Subscriber,并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
observable.subscribe(onNextAction,onErrorAction);
//自动创建 Subscriber,并使用 onNextAction 、 onErrorAction 和 onCompletedAction 来定义
// onNext() 、 onError() 和 onCompleted()
observable.subscribe(onNextAction,onErrorAction,onCompletedAction);

然而,上面都是同步的,要实现异步,则需要用到RxJava的另一个概念:Scheduler。

3、线程控制–Scheduler(一)

在不指定线程的情况下,RxJava遵循的是线程不变的原则,即在那个线程调用subscribe(),就在那个线程生产时间;在那个线程生产事件,就在那个线程消费事件。如果需要切换线程,就需要用到Scheduler(调度器)。

(1)Scheduler的API(一)

在RxJava中,Scheduler———调度器,相当于线程控制器,RxJava通过它来指定每一段代码应该运行在什么线程。RxJava已经内置了几个Scheduler,它们已经适合大多数的使用场景:

  • Scheduler.immediate():直接在当前线程运行,相当于不指定线程。这也是默认的。
  • Scheduler.newThread():总是启用新线程,并在新线程执行操作。
  • Scheduler.io():IO操作(读写文件、读写数据库、网络信息交互等)所使用的Scheduler。行为模式和 newThread() 差不多,区别在于 io()的内部实现是使用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io()newThread()更有效率。不要把计算工作放在 io()中,可以避免创建不必要的线程。
  • Scheduler.computation():计算所使用的Scheduler。这个计算指的是CPU密集型计算,即不会被IO等操作限制性能的操作,例如图形的计算。这个Scheduler所使用的固定的线程池,大小为CPU核数。不要把IO操作放在computation()中,否则IO操作的等待事件会浪费CPU。
  • 另外,Android还有一个专用的AndroidSchedulers.mainThread(),它指定的操作将在Android主线程运行。

有了这几个Scheduler,就可以使用subscribeOn()observeOn()两个方法来对线程进行控制了。

  • subscribeOn():指定subscribe()所发生的线程,即Observable.OnSubscribe()被激活时所处的线程,或者叫做事件产生的线程。
  • observeOn():指定Subscriber所运行在的线程。或者叫做事件消费的线程。

上代码:

Observable.just(1, 2, 3, 4)
    .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程,意思是被创建的事件内容1,2,3,4会在io线程发出。
    .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程,意思是subscriber数字的打印将发生在主线程。
    .subscribe(new Action1<Integer>() {
        @Override
        public void call(Integer number) {
            Log.d(tag, "number:" + number);
        }
    });

上面这段代码中,适用于多数的后台线程读取数据,主线程显示的策略。

(2)Scheduler的原理(一)

放在后面,它的原理是以变换的原理作为基础的。

4、变换

RxJava提供了对事件序列进行变换的支持,这是它的核心功能之一。所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。

(1)API

首先看一个map()的例子:

observable.just("images/logo.png")
                .map(new Func1<String, Bitmap>() {
                    @Override
                    public Bitmap call(String s) {
                        return getBitmapFromPath(s);
                    }
                })
                .subscribe(new Action1<Bitmap>() {
                    @Override
                    public void call(Bitmap bitmap) {
                        showBitmap(bitmap);
                    }
                });

这里出现了一个叫做Func1的类。它和Action1非常相似,也是RxJava的一个借口,用于包装含有一个参数的方法。Func1和Action1的区别在于,Func1包装的是有返回值的方法。另外,和ActionX一样,FuncX也有多个,用于不同参数个数的方法。

可以看到,map()方法将参数中的String对象转换成一个Bitmap对象后返回,而在经过map()方法后,事件的参数类型也有String转换为了Bitmap。这种直接变换对象并返回的,是最常见的也是最容易理解的变换。不过RxJava的比那还远不止这样,它不仅可以针对事件对象,还可以针对整个事件队列,这使得RxJava变得非常灵活。列举几个常用的变换:

  • map():如上,事件对象的直接变换。

这里写图片描述

  • flatMap():这是一个很有用但是非常难理解的变换。首先假设由这么一种需求:假设有一个数据结构【学生】,现在需要打印出一组学生的名字。实现方式非常简单:

        Student[] students = ...;
          Subscriber<String> subscriber = new Subscriber<String>() {
              @Override
              public void onNext(String s) {
                  Log.d(tag,s);
              }
              ...
          };
          Observable.from(students)
                  .map(new Func1<Student, String>() {
                      @Override
                      public String call(Student student) {
                          return student.getName();
                      }
                  })
                  .subscribe(subscriber);

    很简单,再假设:如果要打印出每个学生所需要修的所有课程的名称呢?(需求的区别在于,每个学生只有一个名字,但却有多个课程。)首先可以这样实现:

        Student[] students = ...;
          Subscriber<Student> subscriber = new Subscriber<Student>() {
              @Override
              public void onNext(Student student) {
                  List<Course> courses = student.getCourses();
                  for (int i = 0; i < courses.size(); i++) {
                      Course course = courses.get(i);
                      Log.d(tag, course.getName());
                  }
              }
            ...
          };
          Observable.from(students)
                  .subscribe(subscriber);
      }

    依然很简单,那么如果我不想在Subscriber中使用for循环,而是希望Subscriber中直接传入单个的Course对象呢(这对于代码复用很重要)。用map()显然是不行的,因为map()是一对一的转换。那怎么才能把一个Student转化成多个Course呢?

    这时候,就要用到flatMap()了:

        Student[] students = ...;
          Subscriber<Course> subscriber = new Subscriber<Course>() {
              @Override
              public void onCompleted() {
    
              }
    
              @Override
              public void onError(Throwable e) {
    
              }
    
              @Override
              public void onNext(Course course) {
                  Log.d(TAG,course.getName());
              }
          };
          Observable.from(students)
                  .flatMap(new Func1<Student, Observable<Course>>() {
                      @Override
                      public Observable<Course> call(Student student) {
                          return Observable.from(student.getCourses());
                      }
                  })
                  .subscribe(subscriber);

    从上面的代码可以看出,flatMap()map()有一个相同点:它也是把传入的参数转化之后返回另一个对象。但需要注意的,和map()不同的是,flatMap()中返回的是个Observable对象,并且这个Observable对象并不是北直接发送到Subscriber的回调方法中。

    flatMap()的原理是这样的:

    1. 使用传入的事件对象创建一个Observable对象;
    2. 并不发送这个Observable对象,而是将它激活,于是它开始发送事件;
    3. 每一个创建出来的Observable发送的事件,都被汇入同一个Observable,而这个Observable负责将这些事件统一交给Subscriber的回掉方法。

    这三个步骤,把事件拆分成两级,通过一组新创建的Observable将初始的对象【铺平】之后通过统一路径分发了下去。而这个【铺平】就是flatMap()所谓的flat。

    flatMap()示意图:

这里写图片描述

扩展:由于可以在嵌套的Observable中添加异步代码,flatMap()也常用语嵌套的异步操作,例如嵌套的网络请求。示例代码(Retrofit+RxJava):

java
networkClient.token()
.flatMap(new Func1<String,Observable<Messages>>(){
@Override
public Observable<Messages> call(String token) {
// 返回 Observable<Messages>,在订阅时请求消息列表,并在响应后发送请求到的消息列表
return networkClient.messages();
}
})
.subscribe(new Action1<Messages>(){
@Override
public void call(Messages messages) {
// 处理显示消息列表
showMessages(messages);
}
});

传统的嵌套请求需要使用嵌套的Callback来实现。而通过flatMap(),可以把嵌套的请求写在一条链中,从而保持程序逻辑的清晰。

  • throttleFirst():在每次事件触发后的一定时间间隔内丢弃新的事件。常用作去抖动过滤,例如按钮的点击监听器:RxJava.clickEvents(button) //RxBinding代码。 .throttleFirst(500,Timeunit.MILLISECONDS)//设置防抖间隔为500ms .subscribe(subscriber)。不必再但因用户手都点来两个重复的界面了。

    RxJava还提供很多便捷的方法来实现事件序列的变换。就不一一列举了。

(2)变换的原理:lift()

这些变换虽然功能各有不同,但实质上都是针对事件序列的处理和再发送。而在RxJava内部,它们是基于同一个基础的变换方法lift(Operator)。首先看一下lift()的内部实现(仅核心代码):

//注意:这里不是lift()的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
public <R> Observable<R> lift(Operator<? extends R,? super T> operator){
  return Observable.create(new onSubscribe<R>(){
    @Override
    public void call(Subscriber subscriber){
      Subscriber newSubscriber = operator.call(subscriber);
      newSubscriber.onStart();
      onSubscribe.call(newSubscriber);
    }
  });
}

这段代码很有意思:它生成了一个新的Observable并返回,而且创建新的Observable所用的参数OnSubscribe的回调方法call()的实现竟然和前面说过的Observable.subscribe()一样!然而它们并不一样哦,不一样的地方关键就在于第二行onSubscribe.call(subscriber) 中的OnSubscribe**所指代的对象不同**

  • subscribe()中这句话的 OnSubscribe 指的是 Observable 中的 onSubscribe 对象,这个没有问题,但是lift()之后的情况就复杂了点。

  • 当有lift()时:

    1. lift()创建了一个 Observable 后,加上之前原始的 Observable,已经有两个 Observable 了;
    2. 而同样的,新 Observable 里的新 OnSubscribe 加上之前的原始的 Observable 中的原始 OnSubscribe ,也就有了两个 OnSubscribe;
    3. 当用户调用经过 lift()后的 Observable 的 subscribe()的时候,使用的是lift()所返回的新的 Observable,于是它触发的onSubscribe.call(subscriber),也是用的新 OnSubscribe,即在lift()中生成的那个 OnSubscribe;
    4. 而这个新的 OnSubscribe 的 call()方法中的 onSubscribe,就是指的原始 Observable 中的原始 OnSubscribe,在这个call()方法里,新 OnSubscribe 利用operator.call(subscriber)生成了一个新的 Subscriber( Operator 就是在这里,通过自己的call()方法将新 Subscriber 和原始 Subscriber 进行关联,并插入自己的【变换】代码以实现变换),然后利用这个新的 Subscriber 向原始 Observable 进行订阅。

    这样就实现了lift()过程,有点像一种代理机制,通过事件拦截和处理实现事件序列的变换。

精简掉细节的话,也可以这么说:在 Observable 执行了 lift(Operator)方法之后,会返回一个新的 Observable,这个新的 Observable 会像一个代理一样,负责接收原始的 Observable 发出的事件,并在处理之后发送给 Subscriber。

如图:

这里写图片描述

两次和多次的lift()同理,如下图:

这里写图片描述

举一个具体的 Operator 的实现。下面这是一个将事件中的 Integer 对象转换成 String 的例子,仅供参考:

Observable.lift(new Observable.Operator<String,Integer>(){
  @Override
  public Subscriber<? super Integer> call(final Subscriber<? super String> subscriber){
    //将事件序列中的Integer对象转换为String对象
    return new Subscriber<Integer>{
      @Override
      public void onNext(Integer integer){
        subscriber.onNext("" + integer);
      }

      @Override
      public void onCompleted(){
        subscriber.onCompleted();
      }

      @Override
      public void onError(Throwable e){
        subscriber.onError(e);
      }
    };
  }
});

讲述lift()的原理只是为了更好的了解RxJAva,从而可以更好的使用它。然而不管是否了解lift()的原理,RxJava都不建议开发者自定义Operator来直接使用lift(),而是建议使用已有的lift()包装方法(如map() flatMap()等)进行组合来实现需求,因为直接使用lift()非常容易发生一些难以发现的错误。

(3)compose: 对Observable整体的变换·

除了lift()之外,Observable还有一个变换方法叫做compose(Transformer)。它和lift()的区别在于,lift()是针对事件项和事件序列的,而compose()是针对Observable自身进行变换。举个例子,假设在程序中有多个Observable,并且他们都需要应用一组相同的lift()变换。可以这么写:

observable1
  .lift1()
  .lift2()
  .lift3()
  .lift4()
  .subscribe(subscriber1);
observable2
  .lift1()
  .lift2()
  .lift3()
  .lift4()
  .subscribe(subscriber2);
observable3
  .lift1()
  .lift2()
  .lift3()
  .lift4()
  .subscribe(subscriber3);
observable4
  .lift1()
  .lift2()
  .lift3()
  .lift4()
  .subscribe(subscriber4);

这样太不软件工程了,于是你改成了这样:

private Observable liftAll(Observable observable){
  return observable
        .lift1()
        .lift2()
        .lift3()
        .lift4();
}
...
liftAll(observable1).subscribe(subscriber1);
liftAll(observable2).subscribe(subscriber2);
liftAll(observable3).subscribe(subscriber3);
liftAll(observable4).subscribe(subscriber4);

可读性、可维护性都提高了。可是Observable被一个方法包起来,这种方式对于Observable的灵活性似乎还是增添了那么点限制。怎么办?这个时候,就应该用compose()来解决了:

public class LiftAllTransfomer implements Observable.Transformer<Integer,String>{
  @Override
  public Observable<String> call(Observable<Integer> observable){
    return observable
      .lift1()
      .lift2()
      .lift3()
      .lift4();
  } 
}
...
Transformer liftAll = new LiftAllTransformer();
observable1.compose(liftAll).subscribe(subscriber1);
observable2.compose(liftAll).subscribe(subscriber2);
observable3.compose(liftAll).subscribe(subscriber3);
observable4.compose(liftAll).subscribe(subscriber4);

像上面这样,使用compose()方法,Observable可以利用传入的Transformer对象的call()方法直接对自身进行处理,也就不必被包在方法里面了。

5、线程控制:Scheduler(二)

除了灵活的变换,RxJava另一个牛逼的地方,就是线程的自由控制。

(1)Scheduler的API(二)

前面说过了,可以利用subscribeOn()结合observeOn()来实现线程控制,让事件的产生和消费发生在不同的线程。可是在了解map() flatMap()等变换方法后,有些人就问了:能不能多切换几次线程?

答案是:能。因为observeOn()指定的是Subscriber的线程,而这个Subscriber并不是(严格来说应该是【不一定是】,但这里不妨理解为【不是】)subscribe()参数中的Subscriber,而是observeOn()执行时的当前Observable所对应的Subscriber,即它的直接下级Subscriber。换句话说,observeOn()指定的是它之后的操作所在的线程。因此如果有多次线程切换的需求,只要在每个想要切换线程的位置调用依次observeOn()即可。上代码:

Observable.just(1,2,3,4)//IO线程,由subscribeOn()指定
  .subscribeOn(Schedulers.io())
  .observeOn(Schedulers.newThread())
  .map(mapOperator)//新线程,由observeOn()指定
  .observeOn(Schedulers.io())
  .map(mapOperator2)//IO线程,由observeOn()指定
  .observeOn(AndroidSchedulers.mainThread)
  .subscribe(subscriber);//Android组合县城,由observeOn()指定

如上,通过observeOn()的多次调用,程序实现了线程的多次切换。

不过,不同于observeOn(),subscribeOn()的位置放在那里都可以,但它是只能调用一次的。

(2)Scheduler的原理(二)

其实,subscribeOn()observeOn()的内部实现,也是用的lift()。具体看图(不同颜色的箭头表示不同的线程):

subscribeOn()原理图:

这里写图片描述

observeOn()原理图:

这里写图片描述

从图中可以看出,subscribeOn()observeOn()都做了线程切换的工作(图中的”schedule…”部位)。不同的是,subscribeOn()的线程切换发生在 OnSubscribe 中,即在它通知上一级 OnSubscribe 时,这时事件还没有开始发送,因此subscribeOn()的线程控制可以从事件发出的开端就造成影响;而observeOn()的线程切换则发生在它内建的 Subscriber 中,即发生在它即将给下一级 Subscriber 发送事件时,因此observeOn()控制的是它后面的线程。

最后,用一张图来解释当多个subscribeOn()observeOn()混合使用时,线程调度时怎么发生的(由于图中对象较多,相对于上面的图对结构做了一些简化调整):

这里写图片描述

图中共有5处含有对事件的操作。由图中可以看出,1和2两处受第一个subscribeOn()影响,运行在红色线程;3和4受第一个observeOn()的影响,运行在绿色线程;5处受第二个onserveOn()的影响,运行在紫色线程;而第二个subscribeOn()由于在通知过程就被第一个subscribeOn()截断,因此对整个流程并没有任何影响。所以:当使用了多个subscribeOn()的时候,只有第一个起作用。

(3)延伸:doOnSubscribe()

然而,虽然超过一个的subscribeOn()对事件处理的流程没有影响,但在流程之前却是可以利用的。

在前面说到Subscriber的时候,提到过Subscriber的onStart()可以用作流程开始前的初始化。然而onStart()由于在subscribe()发生时就被调用了,因此不能指定线程,而是只能执行在subscribe()被调用的线程。这就导致如果onStart()中含有对线程有要求的代码(例如在界面上显示一个ProgressBar,这必须在主线程中执行),将会有线程非法的风险,因为有时你无法预测subscribe()将会在什么线程执行。

而与subscriber.onStart()相对应的,有一个方法Observable.doOnSubscribe()。它和Subscriber.onStart()同样是在subscribe()调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下,doOnSubscribe()执行在subscribe()发生的线程;而如果在doOnSubscribe()之后有subscribeOn()的话,它将执行在离它最近的subscribeOn()所指定的线程。

示例代码:

Observable.create(onSubscribe)
    .subscribeOn(Schedulers.io())
    .doOnSubscribe(new Action0() {
        @Override
        public void call() {
            progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行
        }
    })
    .subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(subscriber);

如上,在doOnSubscribe()的后面跟一个subscribeOn(),就能指定准备工作的线程了。

RxJava的适用场景和使用方式

1、与Retrofit的结合

Retrofit 是 Square 的一个著名的网络请求库。

Retrofit 除了提供了传统的 Callback 形式的API,还有 RxJava 版本的 Observable 形式的API。下面用对比的方式来介绍 Retrofit 的 RxJava 版 API 和传统版本的区别。

以获取一个 User 对象的接口作为例子。使用 Retrofit 的传统 API,可以用这样的方式定义请求:

@GET("/user")
public void getUser(@Query("userId") String userId,Callback<user> callback);

在程序的构建过程中,Retrofit 会自动把方法实现并生成代码,然后开发者可以利用下面的方法来获取特定用户并处理响应:

getUser(userId,new Callback<User>(){
  @Override
  public void success(User user){
    userView.setUser(user);
  }
  @Override
  public void failure(RetrofitError error){
    //error handing
    ...
  }
});

而使用RxJava形式的API,定义同样的请求是这样的:

@GET("/user")
public Observable<User> getUser(@Query("userId") String userId);

使用的时候是这样的:

getUser(userId)
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Observer<User>(){
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }

        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
  });

区别就在于Retrofit把请求封装进Observable,在请求结束后调用onNext()或在请求失败后调用onError()。但本质都差不多,而且在细节上 Observable的形式似乎还比Cakkback形式要差一些,那Retrofit为什么还要提供RxJava的支持呢?

因为它好用啊,从这个例子看不出来因为这只是最简单的情况。而情况一旦复杂起来,Callback形式马上就会开始让人头疼。

比如,这样一种情况:你的程序渠道的User并不应该直接显示,而是需要先于数据库中的数据进行比对和修正后再显示。使用Callback方式大概可以这么写:

getUser(userId,new Callback<User>(){
    @Override
    public void success(User user) {
        processUser(user); // 尝试修正 User 数据
        userView.setUser(user);
    }

    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }

});

很简便,但是不要这样做,为什么?因为这样做会影响性能。数据库的操作很重,一次读写操作花费10ms-20ms是很常见的,这样的耗时操作容易造成界面卡顿。所以通常情况下,如果可以的话一定要避免在主线程中处理数据库。所以为了提升性能,这段代码可以优化以下:

getUser(userId, new Callback<User>() {
    @Override
    public void success(User user) {
        new Thread() {
            @Override
            public void run() {
                processUser(user); // 尝试修正 User 数据
                runOnUiThread(new Runnable() { // 切回 UI 线程
                    @Override
                    public void run() {
                        userView.setUser(user);
                    }
                });
            }).start();
    }

    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
});

性能问题解决,但是单吗就变得比较乱了,蜜汁缩进!

看RxJava的形式:

getUser(userId)
  .doOnNext(new Action1<User>(){
        @Override
        public void call(User user) {
            processUser(user);
        }
  })
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Observer<User>(){
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }

        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
  });

2、RxBinding

RxBinding是Jake Wharton 的一个开源库,它提供了一套在 Android 平台上的基于 RxJava 的 Binding API。所谓 Binding,就是类似设置 OnClickListener、设置TextWatcher这样的注册绑定对象API。

举个设置点击监听的例子。使用RxBinding,可以把事件监听用这样的方法来设置:

Button button = ...;
RxView.clickEvents(button) // 以 Observable 形式来反馈点击事件
    .subscribe(new Action1<ViewClickEvent>() {
        @Override
        public void call(ViewClickEvent event) {
            // Click handling
        }
    });

看起来除了形式变了没什么区别,实质上也是这样。甚至如果你看一下它的源码,你会发现它连实现都没什么惊喜:它的内部是直接用一个包裹着的 setOnClickListener() 来实现的。然而,仅仅这一个形式的改变,却恰好就是 RxBinding 的目的:扩展性。通过 RxBinding把点击监听转换成 Observable 之后,就有了对它进行扩展的可能。扩展的方式有很多,根据需求而定。一个例子是前面提到过的 throttleFirst() ,用于去抖动,也就是消除手抖导致的快速连环点击:

RxView.clickEvents(button)
    .throttleFirst(500,TimeUnit.MILLISECONDS)
    .subscribe(clickAvction);

3、各种异步操作

前面举的 RetrofitRxBinding 的例子,是两个可以提供现成的 Observable 的库。而如果你有某些异步操作无法用这些库来自动生成 Observable,也完全可以自己写。例如数据库的读写、大图片的载入、文件压缩/解压等各种需要放在后台工作的耗时操作,都可以用 RxJava 来实现,有了之前几章的例子,这里应该不用再举例了。

4、RxBus

RxBus 名字看起来像一个库,但它并不是一个库,而是一种模式,它的思想是使用 RxJava 来实现了 EventBus ,而让你不再需要使用 Otto 或者 GreenRobot 的 EventBus。至于什么是 RxBus,可以看这篇文章。顺便说一句,Flipboard 已经用 RxBus 替换掉了 Otto ,目前为止没有不良反应。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值