在这篇文章中我们来了解一些辅助操作符,可以让我们更加地方便地处理Observable。
一、Delay
顾名思义,Delay操作符就是让发射数据的时机延后一段时间,这样所有的数据都会依次延后一段时间发射。在Rxjava中将其实现为Delay和DelaySubscription。不同之处在于Delay是延时数据的发射,而DelaySubscription是延时注册Subscriber。
下面我们使用Delay和DelaySubscribtion操作符来延迟两个Observable数据的发射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | private Observable<Long> delayObserver() { return createObserver(2).delay(2000, TimeUnit.MILLISECONDS); } private Observable<Long> delaySubscriptionObserver() { return createObserver(2).delaySubscription(2000, TimeUnit.MILLISECONDS); } private Observable<Long> createObserver(int index) { return Observable.create(new Observable.OnSubscribe<Long>() { @Override public void call(Subscriber<? super Long> subscriber) { log("subscrib:" + getCurrentTime()); for (int i = 1; i <= index; i++) { subscriber.onNext(getCurrentTime()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).subscribeOn(Schedulers.newThread()); } private long getCurrentTime() { return System.currentTimeMillis()/1000; } |
分别对其进行注册
1 2 3 4 5 6 7 8 9 10 | mLButton.setText("delay"); mLButton.setOnClickListener(e -> { log("start subscrib:" + getCurrentTime()); delayObserver().subscribe(i -> log("delay:" + (getCurrentTime() - i))); }); mRButton.setText("delaySubscription"); mRButton.setOnClickListener(e -> { log("start subscrib:" + getCurrentTime()); delaySubscriptionObserver().subscribe(i -> log("delaySubscription:" + i)); }); |
运行结果如下。可以看到两个操作符都让我们达到了延迟2秒后再发射数据的目的
二、Do
Do操作符就是给Observable的生命周期的各个阶段加上一系列的回调监听,当Observable执行到这个阶段的时候,这些回调就会被触发。在Rxjava实现了很多的doxxx操作符。
DoOnEach可以给Observable加上这样的样一个回调:Observable每发射一个数据的时候就会触发这个回调,不仅包括onNext还包括onError和onCompleted。
DoOnNext则只有onNext的时候才会被触发。
doOnSubscribe和doOnUnSubscribe则会在Subscriber进行订阅和反订阅的时候触发回调。当一个Observable通过OnError或者OnCompleted结束的时候,会反订阅所有的Subscriber。
DoOnError会在OnError发生的时候触发回调,并将Throwable对象作为参数传进回调函数里。
DoOnComplete会在OnCompleted发生的时候触发回调。
DoOnTerminate会在Observable结束前触发回调,无论是正常还是异常终止。
finallyDo会在Observable结束后触发回调,无论是正常还是异常终止。
好了,介绍了这么多do的操作符,我们接下来创建两个Observable对象,并分别用上面的一系列do操作符进行注册回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | private Observable<Integer> doOnEachObserver() { return Observable.just(1, 2, 3) .doOnEach(notification -> log("doOnEach send " + notification.getValue() + " type:" + notification.getKind())) .doOnNext(aInteger -> log("doOnNext send " + aInteger)) .doOnSubscribe(() -> log("on subscribe")) .doOnUnsubscribe(() -> log("on unsubscribe\n")) .doOnCompleted(() -> log("onCompleted")); } private Observable<Integer> doOnErrorObserver() { return createObserver() .doOnEach(notification -> log("doOnEach send " + notification.getValue() + " type:" + notification.getKind())) .doOnError(throwable -> log("OnError:" + throwable.getMessage())) .doOnTerminate(() -> log("OnTerminate")) .finallyDo(() -> log("finallyDo")); } private Observable<Integer> createObserver() { return Observable.create(new Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { for (int i = 1; i <= 5; i++) { if (i <= 3) { subscriber.onNext(i); } else { subscriber.onError(new Throwable("num>3")); } } } }); } |
分别进行订阅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | mLButton.setText("do"); mLButton.setOnClickListener(e -> doOnEachObserver().subscribe(i -> log("do:" + i))); mRButton.setText("doOnError"); mRButton.setOnClickListener(e -> doOnErrorObserver().subscribe(new Subscriber<Integer>() { @Override public void onCompleted() { log("onCompleted"); } @Override public void onError(Throwable e) { log("subscriber onError:" + e.getMessage()); } @Override public void onNext(Integer integer) { log("subscriber onNext:" + integer); } })); |
三、Meterialize
Meterialize操作符将OnNext/OnError/OnComplete都转化为一个Notification对象并按照原来的顺序发射出来
而DeMeterialize则是执行相反的过程。
下面我们使用这两个操作符来处理两个Observable对象
1 2 3 4 5 6 7 | private Observable<Notification<Integer>> meterializeObserver() { return Observable.just(1, 2, 3).materialize(); } private Observable<Integer> deMeterializeObserver() { return eterializeObserver().dematerialize(); } |
分别进行订阅
1 2 3 4 | mLButton.setText("meterialize"); mLButton.setOnClickListener(e -> meterializeObserver().subscribe(i -> log("meterialize:" + i.getValue() + " type" + i.getKind()))); mRButton.setText("deMeterialize"); mRButton.setOnClickListener(e -> deMeterializeObserver().subscribe(i->log("deMeterialize:"+i))); |
运行结果如下所示,可以看到onComplete也被meterialize包装后发射了出来,onError也同样。
四、SubscribOn/ObserverOn
这两个操作符在前面的例子中我们已经使用过多次了,使用起来十分方便。在android开发中,相信大家一定都遇到过不能在主线程修改UI的问题,所以不得不使用Handler、AsyncTask等来更新UI界面。使用SubscribOn和ObserverOn操作符,各种线程的问题都将变得十分地简单。
SubscribOn用来指定Observable在哪个线程上运行,我们可以指定在IO线程上运行也可以让其新开一个线程运行,当然也可以在当前线程上运行。一般来讲会指定在各种后台线程而不是主线程上运行,就如同AsyncTask的doInBackground一样。
ObserverOn用来指定观察者所运行的线程,也就是发射出的数据在那个线程上使用。在android中,如果我们要修改UI界面,观察者就必须在主线程上运行,就如同AsyncTask的onPostExecute。
下面创建两个Observable并使用ObserverOn和SubscribOn使Observable和观察者运行在不同的线程上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | private Observable<Integer> observerOnObserver() { return createObserver() .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.newThread()); } private Observable<Integer> subscribeOnObserver() { return createObserver() .subscribeOn(Schedulers.computation()) .observeOn(Schedulers.immediate()); } private Observable<Integer> createObserver() { return Observable.create(new Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { log("on subscrib:" + Thread.currentThread().getName()); subscriber.onNext(1); subscriber.onCompleted(); } }); } |
分别进行订阅
1 2 3 4 | mLButton.setText("observerOn"); mLButton.setOnClickListener(e -> observerOnObserver().subscribe(i -> log("observerOn:" + Thread.currentThread().getName()))); mRButton.setText("subscribeOn"); mRButton.setOnClickListener(e -> subscribeOnObserver().subscribe(i -> log("subscribeOn:" + Thread.currentThread().getName()))); |
五、TimeInterval\TimeStamp
TimeInterval会拦截发射出来的数据,取代为前后两个发射两个数据的间隔时间。对于第一个发射的数据,其时间间隔为订阅后到首次发射的间隔。
TimeStamp会将每个数据项给重新包装一下,加上了一个时间戳来标明每次发射的时间
下面使用这两个操作符来处理两个Observable对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | private Observable<TimeInterval<Integer>> timeIntervalObserver() { return createObserver().timeInterval(); } private Observable<Timestamped<Integer>> timeStampObserver() { return createObserver().timestamp(); } private Observable<Integer> createObserver() { return Observable.create(new Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { for (int i = 0; i <= 3; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } subscriber.onNext(i); } subscriber.onCompleted(); } }).subscribeOn(Schedulers.newThread()); } |
分别进行订阅
1 2 3 4 | mLButton.setText("timeInterval"); mLButton.setOnClickListener(e -> timeIntervalObserver().subscribe(i -> log("timeInterval:" + i.getValue()+"-"+i.getIntervalInMilliseconds()))); mRButton.setText("timeStamp"); mRButton.setOnClickListener(e -> timeStampObserver().subscribe(i -> log("timeStamp:" + i.getValue()+"-"+i.getTimestampMillis()))); |
六、Timeout
Timeout操作符给Observable加上超时时间,每发射一个数据后就重置计时器,当超过预定的时间还没有发射下一个数据,就抛出一个超时的异常。
Rxjava将Timeout实现为很多不同功能的操作符,比如说超时后用一个备用的Observable继续发射数据等。
下面我们创建一个Observable,逐渐加大间隔地发射数据,并使用timeout加上超时的限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | private Observable<Integer> timeoutObserver() { return createObserver().timeout(200, TimeUnit.MILLISECONDS); } private Observable<Integer> timeoutobserverObserver() { return createObserver().timeout(200, TimeUnit.MILLISECONDS, Observable.just(5, 6)); } private Observable<Integer> createObserver() { return Observable.create(new Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { for (int i = 0; i <= 3; i++) { try { Thread.sleep(i * 100); } catch (InterruptedException e) { e.printStackTrace(); } subscriber.onNext(i); } subscriber.onCompleted(); } }); } |
分别进行订阅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | mLButton.setText("timeout"); mLButton.setOnClickListener(e -> timeoutObserver().subscribe(new Subscriber<Integer>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { log(e); } @Override public void onNext(Integer integer) { log("timeout:" + integer); } })); mRButton.setText("timeoutobserver"); mRButton.setOnClickListener(e -> timeoutobserverObserver().subscribe(i -> log(i))); |
七、Using
Using操作符创建一个在Observable生命周期内存活的资源,也可以这样理解:我们创建一个资源并使用它,用一个Observable来限制这个资源的使用时间,当这个Observable终止的时候,这个资源就会被销毁。
Using需要使用三个参数,分别是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | private Observable<Long> usingObserver() { return Observable.using(() -> new Animal(), i -> Observable.timer(5000,TimeUnit.MILLISECONDS), o -> o.relase()); } private class Animal { Subscriber subscriber = new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Object o) { log("animal eat"); } }; public Animal() { log("create animal"); Observable.interval(1000, TimeUnit.MILLISECONDS) .subscribe(subscriber); } public void relase() { log("animal released"); subscriber.unsubscribe(); } } |
对其进行订阅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Observable<Long> observable = usingObserver(); Subscriber subscriber = new Subscriber() { @Override public void onCompleted() { log("onCompleted"); } @Override public void onError(Throwable e) { log("onError"); } @Override public void onNext(Object o) { log("onNext"+o); } }; mLButton.setText("using"); mLButton.setOnClickListener(e -> observable.subscribe(subscriber)); mRButton.setText("unSubscrib"); mRButton.setOnClickListener(e -> subscriber.unsubscribe()); |
运行结果如下。在订阅了几秒之后,对其进行反订阅,Observable就会终止从而触发Animal的释放。
关于辅助操作符就到这里了,本文的demo程序见github