RxJava2详解(一)--基础

简介

什么是ReactiveX

ReactiveX: An API for asynchronous programming with observable streams.

ReactiveX(Reactive Extensions),一般简写为Rx,是一个使用可观察数据流进行异步编程的编程接口。由微软的架构师Erik Meijer领导的团队开发,Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便地编写异步和基于事件的程序,现在已经有了RxJava、RxJS、Rx.NET、RxScala、RxClojure、RxSwift等基本所有主流语言的实现,受到越来越多的开发者的欢迎,开源社区也普遍在使用。
ReactiveX不仅仅是一个API,它是一种思想、一种编程突破,它影响了许多其它的API、框架甚至编程语言。

为什么要使用ReactiveX

无论你开发app还是后台应用,你总会时不时地编写一些异步或基于事件的代码,但你会发现很难完美地处理工作线程和主线程的切换、异常的处理、线程的取消、线程同步等等问题,而且在多个线程协调处理业务逻辑时代码结构变得异常的复杂而且还容易出错。
使用Rx,你可以:

函数式编程
对可观察数据流使用无副作用的输入输出函数,避免了程序里错综复杂的状态
精简代码
Rx的操作符经常可以将复杂的难题简化为很少的几行代码
更好地处理异步错误
传统的try/catch对于异步计算过程中的错误无能为力,但Rx提供了很好的错误处理机制
轻松处理并发
Rx的Observable和Scheduler让开发者可以摆脱底层的线程同步和各种并发问题

也就是说,ReactiveX的Observable模型让你对异步事件流的处理就像平时对数据集(如数组)进行简单、可组合的处理一样,让你从混乱的回调中解脱出来,写出更高可读性、更不容易产生Bug的代码。
Rx结合了观察者模式、迭代器模式和函数式编程的优秀思想。Rx扩展观察者模式以支持数据/事件序列,添加了一些操作符以使你可以声明式的组合这些序列,而无需关注底层的实现:如线程、同步、线程安全、并发数据结构和非阻塞IO 。

Observable是可组合的
传统的并发处理中, Java Futures技术对于简单的无嵌套异步操作是简单高效的,如 benjchristensen/FuturesA.java。但是一旦涉及到嵌套那么代码复杂度将有一个非常可观的增加,如 benjchristensen/FuturesB.java,一个带条件的异步任务需要等待另一个异步任务拿到的数据才能工作,而这就可能会产生一些其他的异步任务被阻塞的问题。
使用Future很难正确地组合多个带条件的异步流(考虑到运行时各种潜在的问题,甚至可以说是不可能的),当然,如果你对并发编程非常精通还是可以做到的,但是整个代码逻辑也会变得异常复杂且很容易出错,或者过早地阻塞在 Future.get(),这样的话异步执行的优势就完全没有了。或许你会考虑使用回调来解决 Future.get()过早阻塞的问题,但是对于嵌套的异步任务来说,使用回调同样会使代码变得混乱,如 benjchristensen/CallbackB.java。Rx的Observable从另一方面讲就是为了组合异步数据流准备的,使用RX你可以非常方便地组合多个Observable。
Observable是灵活的
Rx的Observable不但可以像Futures一样发射单个值,还可以发射数据序列甚至无穷数据流。也就是说无论数据是怎样的都可以抽象为一个Observable。
Observable拥有Iterable所有灵活、优雅的特点。Iterable是同步的单向pull,Observable是异步的双向push,即Iterable是需要手动遍历获取数据的,Observable是主动发射数据的;Iterable通过 T next()方法获取数据,Observable通过 onNext(T)发射数据;Iterable遇到错误会抛出异常(throws Exception),Observable遇到错误会调用 onError(Exception)方法;Iterable可以利用 !hasNext()方法判断是否完成,Observable通过调用 onCompleted()方法表示完成。
Observable是不固执的
Rx对于一些特定并发或异步源是没有偏见的,Observable可以用任何方式实现,如线程池、事件循环、非阻塞IO、Actor模式,或者任何满足你的需求、偏好或擅长技术的实现。无论你选择怎样实现它,无论底层实现是阻塞的还是非阻塞的,客户端代码都会将所有与Observable的交互当做是异步的。

RxJava/RxAndroid

RxJava是JVM上的ReactiveX实现 – 一个在 Java VM上使用可观测的序列来组成异步的、基于事件的程序的库。
RxJava目前版本是2.1.0,2.x版本相对于1.x版本有了很大的改动,甚至可以说是重写,而1.x版本截止到2017年6月1号不会增加新的操作符,仅仅修复Bug,截止到2018年3月31号完全停止开发维护。所以1.x版本的RxJava就不再关注了。
RxAndroid是Android上的ReactiveX实现–Android上的RxJava bindings。
RxAndroid添加尽可能少的类到RxJava以确保在Android应用中写响应式组件能够简单省事,尤其是它提供的调度器可以很好地调度主线程和任意Lopper线程。

术语

信号(Signal): 作名词时表示onSubscribe, onNext, onComplete, onError, request(n)cancel方法的触发信号,作动词时表示触发一个信号。
请求(Demand): 作名词时表示由Publisher尚未交付(履行)的Subscriber请求的元素的总数。作动词时表示请求更多的元素的行为。
同步(Synchronous): 在调用线程中执行。
正常返回(Return normally): 永远只向调用者返回一个声明类型的值, 向Subscriber发失败信号的唯一合法方法是通过onError方法。
背压(Backpressure): Observable发送消息太快以至于它的操作符或者订阅者不能及时处理相关的消息的场景。

添加依赖

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.0'

几个比较重要的类

Publisher
/**
 * 一个可能无限数量的序列元素的提供者,根据从其Subscriber收到的请求发送它们。
 * `Publisher`可以在不同的时间点动态地服务多个通过`subscribe(Subscriber)`方法订阅的Subscriber。
 *
 * @param  信号元素类型.
 */
public interface Publisher<T> {

    /**
     * 请求Publisher开始数据流.
     * 这是一个可以被多次调用的"工厂方法", 但每次都会开始一个新的Subscription.
     * 每个Subscription将只会为一个Subscriber工作.
     * 一个Subscriber应该只能向一个Publisher订阅一次.
     * 如果Publisher拒绝了订阅尝试或者订阅失败了,它将通过Subscriber#onError发error信号
     *
     * @param s 将消费来自Publisher的信号的Subscriber
     */
    public void subscribe(Subscribersuper T> s);
}

调用Publisher.subscribe(Subscriber)Subscriber的响应可能是的这样的方法调用序列:

onSubscribe onNext* (onError | onComplete)?

也就是说,如果Subscription没被cancel的话,onSubscribe信号将总会被触发,之后根据Subscriber的请求可能有多个onNext信号,之后如果出错的话会有一个onError信号,如果没有元素的话会有一个onComplete信号。
ReactiveX中最重要的概念就是流(stream),任何东西都可以看作是流,最常见的就是网络数据流、UI事件流,产生stream的通常叫作被观察者(Observable)/生产者,而监听、处理stream的通常叫做观察者(Observer)/消费者,两者的关联通常叫作订阅(Subscribe)。
RxJava2详解(一)--基础_第1张图片
上面说的Publisher就是产生事件的事件源,而RxJava根据不同使用情况提供了几个事件源的类,其中Flowable就是Publisher的实现类:

  • io.reactivex.Flowable: 0..N个流,支持Reactive-Streams和背压(backpressure)
  • io.reactivex.Observable: 0..N个流,不支持背压(backpressure)
  • io.reactivex.Single: 一个只包含一个item或error的流
  • io.reactivex.Completable: 一个不包含任何item只包含completion或error信号的流
  • io.reactivex.Maybe: 一个只包含一个maybe value或者error的流

首先来看一下Flowable

import io.reactivex.functions.Consumer;

Flowable.just("Hello world")
  .subscribe(new Consumer() {
      @Override public void accept(String s) {
          System.out.println(s);
      }
  });

Flowable是为了更好地处理背压问题而新设计的类以弥补Observable的不足,也就是说,Observable不支持背压处理,一旦未及时处理的事件数累积到一定程度就会产生MissingBackpressureException或者OutOfMemoryError
如果发射事件的Observable(上游)和接收处理事件的Observer(下游)工作在不同线程,就可能会出现发射事件的速度与处理事件的速度不一样的情况,如果发射的太快,就会出现事件积累的情况,而事件的积累会导致大量的资源浪费甚至OOM。而解决这一情况最直接暴力的方式就是控制上游的发射数量或发射速度,以给下游足够的事件处理事件,但是控制发射数量可能会导致部分事件被丢弃,控制发射速度可能影响性能。所以Flowable采用了更优雅的方式来解决这一问题,下游可以把自己“处理事件的能力”告诉上游,上游根据这个来决定要不要继续发射事件,也就是说下游可以根据自身情况主动通知上游发送事件,这也就完美解决了上下游流速不均衡的问题。
Flowable提供了工厂方法create(FlowableOnSubscribe source, BackpressureStrategy mode),可以通过第二个参数指定背压策略,MISSING表明由下游处理事件溢出问题,一般用于自定义参数的onBackpressureXXX操作符场景,ERROR表明如果下游跟不上上游的流速就抛出MissingBackpressureExceptionBUFFER表明缓冲所有的onNext值直到下游消费,DROP表明如果下游跟不上就丢弃最近的onNext值,LATEST表明如果下游跟不上就只保留最近的onNext值,覆盖之前的值。Flowable默认的bufferSize是128,由系统参数rx2.buffer-size决定。

Observable可以看作是Flowable的阉割版,只是不支持背压逻辑,其它的逻辑、方法与Flowable是基本一样的。ObservableObservableSource接口的实现,而ObservableSource表示一个基本的、不支持背压的Observable源,由Observer消费,ObservableSource只有一个方法void subscribe(Observer observer)用来订阅给定的Observer。也就是说Subscriber订阅FlowableObserver订阅Observable
Observable可以通过非常多的工厂方法和操作符构建,如通过just()操作符构建:

Observable.just("one", "two", "three", "four", "five")
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new DisposableObserver() {
            @Override
            public void onNext(String value) {
                Log.d(TAG, "onNext(" + value + ")");
            }
            @Override
            public void onError(Throwable e) {
                Log.e(TAG, "onError()", e);
            }
            @Override
            public void onComplete() {
                Log.d(TAG, "onComplete()");
            }
        });

那什么时候使用Observable,什么时候使用Flowable呢?
何时使用Observable:

  • 最多1000个元素的数据流,即随着时间推移元素数量依然足够少以至于应用几乎没机会出现OOME。
  • 处理诸如鼠标移动或触摸事件之类的GUI事件:这些事件很少会合理地背压,也并不频繁。你可以使用Observable处理频率小于1000 Hz的事件,但最好考虑使用采样/去抖动。
  • 流本质上是同步的,但是您的平台不支持Java Streams,或者你忽略其中的功能。 使用Observable具有比Flowable更低的开销(你也可以考虑为Iterable流优化的IxJava 支持Java 6+)。

何时使用Flowable:

  • 处理以某种方式生成的10k+元素,处理链可以告诉源限制元素生成的数量。
  • 从磁盘读取(解析)文件本质上是阻塞式和基于pull的,你可以很好地控制背压,例如从指定的请求量中读取多少行)。
  • 通过JDBC从数据库读取也是阻塞式和基于pull的,你可以根据每个下游请求调用ResultSet.next()来控制。
  • 网络(流)IO,其中网络帮助或使用的协议支持请求一些逻辑量。
  • 一些阻塞式和/或基于pull的数据源,最终会被一个非阻塞响应式的API/driver使用。

SingleObservable类似但只能发射一个onSuccessonError给它的消费者io.reactivex.SingleObserver

public interface SingleObserver<T> {
    void onSubscribe(Disposable d);
    void onSuccess(T value);
    void onError(Throwable e);
}

响应流的事件模式为:onSubscribe (onSuccess | onError)?

Completable用来表示一个延迟计算且不关心任何数据只关心完成和异常,只能发射一个onCompleteonError给它的消费者o.reactivex.CompletableObserver

public interface CompletableObserver {
    void onSubscribe(Disposable d);
    void onComplete();
    void onError(Throwable e);
}

响应流的事件模式为:onSubscribe (onComplete | onError)?

Maybe从概念上讲是SingleCompletable的结合,提供了一种捕获一些响应源发射0或1个item或一个error的发射模式的手段,用来表示一个延迟计算。MaybeMaybeSource作为基本接口,以MaybeObserver作为信号接收接口。由于最多只能发射一个元素Maybe就没打算支持背压处理,这就意味着,在onSubscribe(Disposable)被调用后可能接着调用一个其它onXXX方法,与Flowable不同的是,如果只有一个值信号,只有onSuccess被调用而不是onComplete,使用这种新的基本响应类型与其他类型几乎是一样的,因为它提供了一个适用于0或1个item序列的Flowable操作符子集。

Maybe.just(1)
.map(v -> v + 1)
.filter(v -> v == 1)
.defaultIfEmpty(2)
.test()
.assertResult(2);

响应流的事件模式为:onSubscribe (onSuccess | onError | onComplete)?

Subscriber
/**
 * 将Subscriber的一个实例传给Publisher#subscribe(Subscriber)方法后,将会收到一次onSubscribe(Subscription)方法调用
 * 在Subscription#request(long)被调用之前不会受到进一步通知
 * 在发送请求信号后:
 * 调用一到多次onNext(Object)方法,最多调用Subscription#request(long)中定义的次数
 * 调用一次onError(Throwable)或onComplete()方法表明终止状态,之后不会发送进一步事件
 * 只要Subscriber实例还能够处理,就可以通过Subscription#request(long)发请求信号
 *
 * @param  信号元素类型
 */
public interface Subscriber<T> {
    /**
     * 在调用Publisher#subscribe(Subscriber)之后调用
     * 直到Subscription#request(long)被调用才开始数据流
     * 每当需要更多数据时,这个Subscriber实例都有责任调用Subscription#request(long)
     * Publisher只有在Subscription#request(long)被调用后才会发送通知
     *
     * @param s 可以通过Subscription#request(long)方法请求数据的Subscription
     */
    public void onSubscribe(Subscription s);

    /**
     * Publisher发送的数据通知,以响应Subscription#request(long)的请求
     *
     * @param t the element signaled
     */
    public void onNext(T t);

    /**
     * 失败的结束状态
     * 即使再次调用Subscription#request(long)也不会再发送进一步事件
     *
     * @param t the throwable signaled
     */
    public void onError(Throwable t);

    /**
     * 成功的结束状态
     * 即使再次调用Subscription#request(long)也不会再发送进一步事件
     */
    public void onComplete();
}

为了在构建stream消费者时有更少的内部状态,Rxjava2为Flowable(和Observable)分别定义了DefaultSubscriberResourceSubscriberDisposableSubscriber(以及他们的XObserver变体),以提供资源跟踪支持,可以在外部通过dispose()方法cancelled(取消)或disposed(丢弃):

ResourceSubscriber subscriber = new ResourceSubscriber() {
    @Override
    public void onStart() {
        request(Long.MAX_VALUE);
    }

    @Override
    public void onNext(Integer t) {
        System.out.println(t);
    }

    @Override
    public void onError(Throwable t) {
        t.printStackTrace();
    }

    @Override
    public void onComplete() {
        System.out.println("Done");
    }
};

Flowable.range(1, 10).delay(1, TimeUnit.SECONDS).subscribe(subscriber);

subscriber.dispose();

为了能更好的控制订阅关系,每个基本响应类都提供了E subscribeWith(E subscriber)方法以返回订阅的subscriber/observer:

CompositeDisposable composite2 = new CompositeDisposable();

composite2.add(Flowable.range(1, 5).subscribeWith(subscriber));

在管理请求时有一点需要注意,在Subscriber.onSubscribeResourceSubscriber.onStart方法中调用request(n)可能会马上触发onNext方法(在onSubscribe/onStart方法返回前):

Flowable.range(1, 3).subscribe(new Subscriber() {

    @Override
    public void onSubscribe(Subscription s) {
        System.out.println("OnSubscribe start");
        s.request(Long.MAX_VALUE);
        System.out.println("OnSubscribe end");
    }

    @Override
    public void onNext(Integer v) {
        System.out.println(v);
    }

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

    @Override
    public void onComplete() {
        System.out.println("Done");
    }
});

将会打印:

OnSubscribe start
1
2
3
Done
OnSubscribe end

也就是说,如果在onSubscribe/onStart方法中调用request之后再做一些初始化工作,那么onNext方法可能就看不到初始化的影响。为了避免出现这种情况,要确保onSubscribe/onStart中完成所有的初始化工作再调用request方法。

Subscription
/**
 * Subscription表示Subscriber订阅Publisher的一对一生命周期
 * 它只能由一个Subscriber使用一次
 * 它用于数据请求和取消请求(允许资源清理)
 *
 */
public interface Subscription {
    /**
     * Publisher将不会发送任何事件直到通过该方法发送请求信号
     * 可以随时、频繁地调用,但未完成积累的请求决不能超过Long.MAX_VALUE
     * 未完成积累的请求数为Long.MAX_VALUE可能会被Publisher视为"有效无限"
     * Publisher可以发送的任何请求的信息,只有信号请求可以被安全处理
     * 如果流结束了Publisher发送数量可以少于请求的数量,但是随后必须发射Subscriber#onError(Throwable)或Subscriber#onComplete()
     *
     * @param n 向上游Publisher请求的元素数量(严格正数)
     */
    public void request(long n);

    /**
     * 请求Publisher停止发射数据并且清理资源
     * 由于这个请求是异步的,在调用cancel方法后,数据可能仍然被发送以满足之前的信号请求
     */
    public void cancel();
}

在Android中,对异步任务生命周期的管理尤为重要,而RxJava提供的最重要的接口就是Disposable

public interface Disposable {
    void dispose();
    boolean isDisposed();
}

由于ResourceSubscriberDisposableSubscriber(以及他们的XObserver变体)都实现了Disposable接口,对于单个资源请求我们可以直接调用它的dispose()方法丢弃,对于多个请求,我们就要借助CompositeDisposable来管理了,CompositeDisposable是多个Disposable的容器,提供了复杂度为O(1)的添加删除操作:

public class MainActivity extends Activity {
    private static final String TAG = "RxAndroidSamples";

    private final CompositeDisposable disposables = new CompositeDisposable();

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        findViewById(R.id.button_run_scheduler).setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                onRunSchedulerExampleButtonClicked();
            }
        });
    }

    @Override protected void onDestroy() {
        super.onDestroy();
        disposables.clear();
    }

    void onRunSchedulerExampleButtonClicked() {
        disposables.add(sampleObservable()
            // Run on a background thread
            .subscribeOn(Schedulers.io())
            // Be notified on the main thread
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeWith(new DisposableObserver() {
                @Override public void onComplete() {
                    Log.d(TAG, "onComplete()");
                }

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

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

    static Observable sampleObservable() {
        return Observable.defer(new Callable>() {
          @Override public ObservableSource call() throws Exception {
                // Do some long running operation
                SystemClock.sleep(5000);
                return Observable.just("one", "two", "three", "four", "five");
            }
        });
    }
}

注意,CompositeDisposableclear()方法和dispose()方法类似,clear()可以多次被调用来丢弃容器中所有的Disposable,但dispose()被调用一次后就会失效。
对于订阅生命周期的封装库最常见的是trello/RxLifecycle,不过它需要LifecycleProvider才能工作,大部分情况下你只能选择继承它的RxActivityRxFragment。另一个很有意思的封装是zhihu/RxLifecycle,它利用TakeUtil操作符和一个HeadlessFragment来控制Observable的生命周期。

Processor
/**
 * Processor表示一个处理过程—Subscriber和Publisher都必须遵守两者的契约
 *
 * @param  Subscriber的信号元素类型
 * @param  Publisher的信号元素类型
 */
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}

References

  • ReactiveX.io
  • RxJava Wiki
  • ReactiveX文档中文翻译
  • Piasy对Advanced Reactive Java系列博客的中文翻译

你可能感兴趣的:(Android,RxJava2使用)