Preface:
RxJava 2.0 was officially released on October 29, 2016, and I took the time to study its related features. Strike while the iron is hot. In this article, a brief summary of the use of RxJava 2.0 is given.
Before reading this article, you need to grasp the basic concepts of RxJava 1.0. If you have never touched RxJava, please click Here
RxJava 2.0 VS RxJava 1.0:
1. RxJava 2.0 no longer supports null values, and if a null is passed in, a NullPointerException will be thrown.
Observable.just(null); Single.just(null); Flowable.just(null); Maybe.just(null); Observable.fromCallable(() -> null) .subscribe(System.out::println, Throwable::printStackTrace); Observable.just(1).map(v -> null) .subscribe(System.out::println, Throwable::printStackTrace);
2. All function/action/Consumer interfaces in RxJava 2.0 are designed to throw Exception, which solves the problem that compiler exceptions need to be converted.
3. In RxJava 1.0, Observable can not support backpressure very well. In RxJava 2.0, Oberservable is completely implemented to support backpressure, while Flowable is added to support backpressure. (For the concept of back pressure, please refer to my original English version of ReativeX. Chinese translation)
I. Observable
RxJava 1.0 has four basic concepts: Observable, Observer, subscribe, and event. Observable and Observer implement the subscribe relationship through subscribe() method, so that Observable can send out events to notify Observer when needed.
Based on the above concepts, the basic implementation of RxJava 1.0 has three main points:
Step 1: Create Observer
Observer is the observer, which decides what will happen when an event triggers. The implementation of Observer interface in RxJava:
Observer<String> observer = new Observer<String>() { @Override public void onNext(String s) { Log.d(tag, "Item: " + s); } @Override public void onCompleted() { Log.d(tag, "Completed!"); } @Override public void onError(Throwable e) { Log.d(tag, "Error!"); } };
In addition to the Observer interface, RxJava has built-in an abstract class that implements Observer: Subscriber. Subscriber extends the Observer interface, but their basic usage is exactly the same:
Subscriber<String> subscriber = new Subscriber<String>() { @Override public void onNext(String s) { Log.d(tag, "Item: " + s); } @Override public void onCompleted() { Log.d(tag, "Completed!"); } @Override public void onError(Throwable e) { Log.d(tag, "Error!"); } };
Step 2: Create Observable
Observable is the observee who decides when and what events to trigger. RxJava uses the create() method to create an Observable and defines event triggering rules for it:
Observable observable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello"); subscriber.onNext("Hi"); subscriber.onNext("Aloha"); subscriber.onCompleted(); } });
Step 3: Subscribe (Subscribe)
After creating Observable and Observer, the subscrbe() method is used to connect them, and the whole chain can work. The code form is simple:
observable.subscribe(observer); // Or: observable.subscribe(subscriber);
However, in 2.0, the familiar Subscrber disappeared, replaced by Observable Emitter, commonly known as the transmitter. In addition, since there is no Subscrber, we need to use Observer to create observers. Observer is not the one we are familiar with, and the parameters of Disosable that it calls back are even more difficult to understand.
Step 1: Initialize an Observable
Observable<Integer> observable=Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> e) throws Exception { e.onNext(1); e.onNext(2); e.onComplete(); } });
Step 2: Initialize an Observer
Observer<Integer> observer= new Observer<Integer>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Integer value) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }
Step 3: Establish a subscription relationship
observable.subscribe(observer); //Establish a subscription relationship
It is not difficult to see that there are some differences between RxJava 1.0 and RxJava 1.0. First, when creating Observable, the callback is Observable Emitter, literally the emitter, which is used to transmit data (onNext()) and notification (onError()/onComplete()). Secondly, there is an additional callback method onSubscribe() in the created Observer, passing the parameter Disposable.
Observable Emitter: Emitter means a transmitter, which is used to emit events. It can emit three types of events. By calling onNext(T value), onComplete() and onError (Throwable) of emitter, the next event, complete event and error event can be emitted respectively.
Disposable: The literal meaning of this word is disposable goods, which can be discarded when used up. So how to understand it in RxJava? Corresponding to the example of the pipe above, we can understand it as an organ between two pipes. When its dispose() method is called, it will cut off the two pipes and cause the downstream events not to be received, which is equivalent to Subsciption.
Note: Calling dispose() does not cause the upstream to stop sending events, and the upstream will continue sending the remaining events.
Let's take an example. Let's send 1, 2, complete, 4 upstream in turn. After receiving the second event downstream, cut off the water pipe and see the results of the operation.
Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> emitter) throws Exception { Log.d(TAG, "emit 1"); emitter.onNext(1); Log.d(TAG, "emit 2"); emitter.onNext(2); Log.d(TAG, "emit 3"); emitter.onNext(3); Log.d(TAG, "emit complete"); emitter.onComplete(); Log.d(TAG, "emit 4"); emitter.onNext(4); } }).subscribe(new Observer<Integer>() { private Disposable mDisposable; private int i; @Override public void onSubscribe(Disposable d) { Log.d(TAG, "subscribe"); mDisposable = d; } @Override public void onNext(Integer value) { Log.d(TAG, "onNext: " + value); i++; if (i == 2) { Log.d(TAG, "dispose"); mDisposable.dispose(); Log.d(TAG, "isDisposed : " + mDisposable.isDisposed()); } } @Override public void onError(Throwable e) { Log.d(TAG, "error"); } @Override public void onComplete() { Log.d(TAG, "complete"); } });
The results are as follows:
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: subscribe 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 1 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: onNext: 1 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 2 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: onNext: 2 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: dispose 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: isDisposed : true 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 3 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit complete 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 4
From the running results, we can see that after receiving the onNext 2 event, the water pipe was cut off, but the upstream still sent 3, complete, 4 events, and the upstream did not stop because it sent onComplete. At the same time, you can see that the downstream onSubscibe() method was first invoked.
Disposable is more useful than that. When we talk about thread scheduling later, we will find its importance. With further explanation, we will find it in more places.
In addition, other simplified subscription methods are still retained in RxJava 2.x, and we can choose the corresponding simplified subscription according to our needs. It's just that the incoming object is changed to Consumer. `
Disposable disposable = observable.subscribe(new Consumer<Integer>() { @Override public void accept(Integer integer) throws Exception { //Receive data items here } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { //Receive onError here } }, new Action() { @Override public void run() throws Exception { //Receive onComplete here. } });
Unlike RxJava 1.0, RxJava 2.0 does not have a series of Action/Func interfaces. Instead, it has a functional interface similar to Java 8 named, as follows:
Action is similar to Action0 in RxJava 1.0, except that Action allows exceptions to be thrown.
public interface Action { /** * Runs the action and optionally throws a checked exception * @throws Exception if the implementation wishes to throw a checked exception */ void run() throws Exception; }
Consumer is the consumer, which receives a single value, BigConsumer receives two values, Function is used to transform objects, Predicate is used to judge. Most of these interface names refer to Java 8, and you should know the meaning of the new features of Java 8, so I won't repeat them here.
Thread Scheduling
With regard to thread switching, RxJava 1.x and RxJava 2.x have the same idea of implementation. Here's a brief look at the relevant source code.
1. subscribeOn
Like RxJava 1.x, subscribeOn is used to specify the threads that occur when subscribe() occurs. From the source point of view, internal thread scheduling is achieved through Observable SubscribeOn.
public final Observable<T> subscribeOn(Scheduler scheduler) { ObjectHelper.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler)); }
The core source code of Observable SubscribeOn is in subscribe Actual method. After wrapping Observer with SubscribeOn Observer by proxy, set Disposable to switch subscribe to Scheduler thread.
@Override public void subscribeActual(final Observer<? super T> s) { final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s); s.onSubscribe(parent); //Callback Disposable parent.setDisposable(scheduler.scheduleDirect(new Runnable() { //Setting `Disposable'` @Override public void run() { source.subscribe(parent); //Make Observable subscribe happen in Scheduler threads } })); }
2. observeOn
The observeOn method is used to specify the thread in which the downstream Observer callback occurs.
public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) { //.. //Verification Security return RxJavaPlugins.onAssembly(new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize)); }
The main implementation is subscribe Actual in Observable Observer On. It can be seen that, unlike subscribe On, subscribe operations are not all switched to Scheduler, but through the cooperation of Observer OnSubscriber and Scheduler, through schedule() to switch the threads occurring in downstream Observer callback, which is implemented with RxJava 1.0. Almost the same. The source code for Observer On Subscriber is no longer described here.
@Override protected void subscribeActual(Observer<? super T> observer) { if (scheduler instanceof TrampolineScheduler) { source.subscribe(observer); } else { Scheduler.Worker w = scheduler.createWorker(); source.subscribe(new ObserveOnSubscriber<T>(observer, w, delayError, bufferSize)); } }
Flowable
Flowable is a new class in RxJava 2.0 that is designed to deal with Backpressure problems, but it is not a new concept introduced in RxJava 2.0. The so-called back pressure, that is, the producer's speed is faster than the consumer's speed brings about problems, such as the common click events in Android, click too fast will cause the effect of two clicks.
We know that backpressure control in RxJava 1.0 is done by Observable, using the following:
Observable.range(1,10000) .onBackpressureDrop() .subscribe(integer -> Log.d("JG",integer.toString()));
In RxJava 2.0, it is separated and named Flowable. Therefore, the original Observable is no longer capable of backpressure processing.
Through Flowable, we can customize the backpressure processing strategy.
/** * Represents the options for applying backpressure to a source sequence. */ public enum BackpressureStrategy { /** * OnNext events are written without any buffering or dropping. * Downstream has to deal with any overflow. * <p>Useful when one applies one of the custom-parameter onBackpressureXXX operators. */ MISSING, /** * Signals a MissingBackpressureException in case the downstream can't keep up. */ ERROR, /** * Buffers <em>all</em> onNext values until the downstream consumes it. */ BUFFER, /** * Drops the most recent onNext value if the downstream can't keep up. */ DROP, /** * Keeps only the latest onNext value, overwriting any previous value if the * downstream can't keep up. */ LATEST }
Examples of testing Flowable are as follows:
Flowable.create(new FlowableOnSubscribe<Integer>() { @Override public void subscribe(FlowableEmitter<Integer> e) throws Exception { for(int i=0;i<10000;i++){ e.onNext(i); } e.onComplete(); } }, FlowableEmitter.BackpressureStrategy.ERROR) //Specify backpressure handling strategy, throw exception .subscribeOn(Schedulers.computation()) .observeOn(Schedulers.newThread()) .subscribe(new Consumer<Integer>() { @Override public void accept(Integer integer) throws Exception { Log.d("JG", integer.toString()); Thread.sleep(1000); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Log.d("JG",throwable.toString()); } });
Or it can be controlled in a way similar to RxJava 1.0.
Flowable.range(1,10000) .onBackpressureDrop() .subscribe(integer -> Log.d("JG",integer.toString()));
One thing to note is that Flowable doesn't start sending data as soon as subscriptions are made, but rather wait until Subscription.request() is executed to start sending data. Of course, using the simplified subscribe subscription method specifies Long.MAX_VALUE by default. Examples of manual designation are as follows:
Flowable.range(1,10).subscribe(new Subscriber<Integer>() { @Override public void onSubscribe(Subscription s) { s.request(Long.MAX_VALUE);//Set the number of requests } @Override public void onNext(Integer integer) { } @Override public void onError(Throwable t) { } @Override public void onComplete() { } });
III. Single
Unlike Single Subscriber in RxJava 1.0, Single Observer in RxJava 2.0 has an additional callback method, onSubscribe.
interface SingleObserver<T> { void onSubscribe(Disposable d); void onSuccess(T value); void onError(Throwable error); }
IV. Completable
With Single, Completable has also been redesigned as Reactive-Streams architecture, and Comppletable Subscriber of RxJava 1.0 has been changed to Completable Observer with the following source code:
interface CompletableObserver<T> { void onSubscribe(Disposable d); void onComplete(); void onError(Throwable error); }
V. Subject/Processor
Processor and Subject work the same way. Regarding the Subject section, RxJava 1.0 is not significantly different from RxJava 2.0 in usage, which is not covered here. Processor is a new addition to RxJava 2.0, inherited from Flowable, so it supports backpressure control. Subject does not support backpressure control. Use as follows:
//Subject AsyncSubject<String> subject = AsyncSubject.create(); subject.subscribe(o -> Log.d("JG",o));//three subject.onNext("one"); subject.onNext("two"); subject.onNext("three"); subject.onComplete(); //Processor AsyncProcessor<String> processor = AsyncProcessor.create(); processor.subscribe(o -> Log.d("JG",o)); //three processor.onNext("one"); processor.onNext("two"); processor.onNext("three"); processor.onComplete();
6. Operators
Regarding operators, RxJava 1.0 and RxJava 2.0 are mostly consistent in terms of naming and behavior, with emphasis on subscribe With operators and compose operators.
1. subscribeWith
In RxJava 2.0, subscribe operation no longer returns Subscription, which is now Disposable. To maintain backward compatibility, Flowable provides subscribe With method to return the current observer Subscriber object, and also provides DefaultSubsriber, ResourceSubscriber, Disposable Subscriber interfaces to enable them to Disposable objects are provided to manage their life cycles.
2. compose
RxJava 1.0 Usage:
private static <T> Observable.Transformer<T, T> createIOSchedulers() { return new Observable.Transformer<T, T>() { @Override public Observable<T> call(Observable<T> tObservable) { return tObservable.subscribeOn(Schedulers.io()) .unsubscribeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread()); } }; } public static <T> Observable.Transformer<JsonResult<T>,T> applySchedulers() { return createIOSchedulers(); }
Action1<Integer> onNext = null; String[] items = { "item1", "item2", "item3" }; Subscription subscription = Observable.from(items) .compose(RxUtil.<String>applySchedulers()) .map(new Func1<String, Integer>() { @Override public Integer call(String s) { return Integer.valueOf(s); } }) .subscribe(onNext);
RxJava 2.0 Usage:
public static <T> ObservableTransformer<T, T> io2MainObservable() { return new ObservableTransformer<T, T>() { @Override public ObservableSource<T> apply(Observable<T> upstream) { return upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } }; } public static <T> ObservableTransformer<T, T> applySchedulers() { return io2MainObservable(); }
Consumer<Integer> onNext = null; String[] items = { "item1", "item2", "item3" }; Disposable disposable = Observable.fromArray(items) .compose(RxUtil.<String>applySchedulers()) .map(new Function<String, Integer>() { @Override public Integer apply(String s) throws Exception { return Integer.valueOf(s); } }) .subscribe(onNext);
It can be noted that RxJava 1.0 implements the rx.Observable.Transformer interface, which inherits from Func1<Observable<T>, Observable<R> and 2.0 inherits from io.reactivex.Observable Tansformer<Upstream, Downstream> as a separate interface.
In addition, RxJava 2.0 also provides a Flowable Transformer interface for the compose operator under Flowable, using the following:
public static <T> FlowableTransformer<T, T> io2MainFlowable() { return new FlowableTransformer<T, T>() { @Override public Publisher<T> apply(Flowable<T> upstream) { return upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } }; } public static <T> FlowableTransformer<T, T> applySchedulers() { return io2MainFlowable(); }
Consumer<Integer> onNext = null; Disposable disposable = Flowable.create(new FlowableOnSubscribe<Integer>() { @Override public void subscribe(FlowableEmitter<Integer> e) throws Exception { for(int i=0;i<10000;i++){ e.onNext(i); } e.onComplete(); } }, FlowableEmitter.BackpressureStrategy.ERROR) //Specify backpressure handling strategy, throw exception .compose(RxUtil.<String>applySchedulers()) .subscribe(onNext);