RxJava 操作符系列五

Code4Android發表於2016-12-18

RxJava操作符系列傳送門

RxJava操作符原始碼
RxJava操作符系列一
RxJava操作符系列二
RxJava操作符系列三
RxJava操作符系列四

今天就不囉嗦了,直接開始我們今天的學習。今天介紹一些輔助操作符。

Delay

該操作符讓原始Observable在發射每項資料之前都暫停一段指定的時間。它接受一個定義時長的引數(包括long型資料和單位)。每當原始Observable發射一項資料,delay就啟動一個定時器,當定時器過了給定的時間段時,delay返回的Observable發射相同的資料項。他預設是在computation排程器上執行,當然也有過載方法可以指定排程器,若發射資料後有更新UI操作需將排程器指定AndroidSchedulers.mainThread()。(注意過載方法delay(Fun1),delay(Fun0,Fun1)是預設不在任何特定的排程器上執行)
示例程式碼

Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                Log.e(TAG, "call: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date()));
                subscriber.onNext(1);
                subscriber.onNext(2);
                subscriber.onNext(3);
                subscriber.onNext(4);
                subscriber.onCompleted();
            }
        }).delay(2,TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        Log.e(TAG, "onCompleted: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date()));
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e(TAG, "onError: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date())+e.toString());
                    }

                    @Override
                    public void onNext(Integer integer) {
                        tv1.append("\n"+new SimpleDateFormat("yyyy/MM/ddHH:MM:ss").format(new Date())+"  "+integer);
                        Log.e(TAG, "onNext: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date())+integer);
                    }
                });複製程式碼

輸出日誌資訊

call: 2016/12/17 20:12:07
onNext: 2016/12/17 20:12:091
onNext: 2016/12/17 20:12:092
onNext: 2016/12/17 20:12:093
onNext: 2016/12/17 20:12:094
onCompleted: 2016/12/17 20:12:09複製程式碼

為了讓你看到延遲效果,我把call和onNext()回撥的時間也列印出來,傳送最終資料是延遲兩秒傳送的。

delaySubscription

該操作符也是delay的一種實現,它和dealy的區別是dealy是延遲資料的傳送,而此操作符是延遲資料的註冊,指定延遲時間的過載方法是執行在computation排程器的。為了方便觀察延遲註冊效果,建立Observable變數。如下示例程式碼

       Observable observable = Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                Log.e(TAG, "call: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date()));
                subscriber.onNext(1);
                subscriber.onNext(2);
                subscriber.onNext(3);
                subscriber.onNext(4);
                subscriber.onCompleted();
            }
        });
        Log.e(TAG, "call11: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date()));
        observable.delaySubscription(2,TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        Log.e(TAG, "onCompleted: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date()));
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e(TAG, "onError: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date())+e.toString());
                    }

                    @Override
                    public void onNext(Integer integer) {
                        tv1.append("\n"+new SimpleDateFormat("yyyy/MM/ddHH:MM:ss").format(new Date())+"  "+integer);
                        Log.e(TAG, "onNext: "+new SimpleDateFormat("yyyy/MM/dd HH:MM:ss").format(new Date())+integer);
                    }
                });複製程式碼

輸出日誌資訊

call11: 2016/12/17 20:12:43
call: 2016/12/17 20:12:45
onNext: 2016/12/17 20:12:451
onNext: 2016/12/17 20:12:452
onNext: 2016/12/17 20:12:453
onNext: 2016/12/17 20:12:454
onCompleted: 2016/12/17 20:12:45複製程式碼

Do

對於do系列操作符理解比較容易,他相當於給Observable執行週期的關鍵節點新增回撥。當Observable執行到這個階段的時候,這些回撥就會被觸發。在Rxjava do系列操作符有多個,如doOnNext,doOnSubscribe,doOnUnsubscribe,doOnCompleted,doOnError,doOnTerminate和doOnEach。
當Observable每傳送一個資料時,doOnNext會被首先呼叫,然後再onNext。若發射中途出現異常doOnError會被呼叫,然後onError。若資料正常傳送完畢doOnCompleted會被觸發,然後執行onCompleted。當訂閱或者解除訂閱doOnSubscribe,doOnUnsubscribe會被執行。
示例程式碼

  Observable.just(1, 2, 3)
                .doOnNext(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                        Log.e(TAG, "doOnNext: " );
                    }
                })
                .doOnError(new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.e(TAG, "doOnError: " );
                    }
                })
                .doOnCompleted(new Action0() {
                    @Override
                    public void call() {
                        Log.e(TAG, "doOnCompleted: " );
                    }
                })
                .doOnSubscribe(new Action0() {
                    @Override
                    public void call() {
                        Log.e(TAG, "doOnSubscribe: " );
                    }
                })
                .doOnUnsubscribe(new Action0() {
                    @Override
                    public void call() {
                        Log.e(TAG, "doOnUnsubscribe: " );
                    }
                })
                .doOnTerminate(new Action0() {
                    @Override
                    public void call() {
                        Log.e(TAG, "doOnTerminate: " );
                    }
                })
                .doAfterTerminate(new Action0() {
                    @Override
                    public void call() {
                        Log.e(TAG, "doAfterTerminate: " );
                    }
                })
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        Log.e(TAG, "onCompleted1: ");
                    }

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

                    @Override
                    public void onNext(Integer integer) {
                        Log.e(TAG, "onNext1: " + integer);
                    }
                });複製程式碼

輸出日誌資訊

12-17 23:13:56.151 29946-29946/com.example.xh E/RxJava: doOnSubscribe: 
12-17 23:13:56.151 29946-29946/com.example.xh E/RxJava: doOnNext: 
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: onNext1: 1
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: doOnNext: 
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: onNext1: 2
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: doOnNext: 
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: onNext1: 3
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: doOnCompleted: 
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: doOnTerminate: 
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: onCompleted1: 
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: doOnUnsubscribe: 
12-17 23:13:56.155 29946-29946/com.example.xh E/RxJava: doAfterTerminate:複製程式碼

對於doOnEach操作符,他接收的是一個Observable引數,相當於doOnNext,doOnError,doOnCompleted綜合體,如下示例程式碼

Observable.just(1,2,3)
                .doOnEach(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        Log.e(TAG, "onCompleted: " );
                    }

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

                    @Override
                    public void onNext(Integer integer) {
                        Log.e(TAG, "onNext: "+integer);
                    }
                }).subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {
                Log.e(TAG, "onCompleted1: " );
            }

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

            @Override
            public void onNext(Integer integer) {
                Log.e(TAG, "onNext1: "+integer);
            }
        });複製程式碼

輸出日誌資訊

onNext: 1
onNext1: 1
onNext: 2
onNext1: 2
onNext: 3
onNext1: 3
onCompleted: 
onCompleted1:複製程式碼

#SubscribeOn/ObserveOn
該操作符指定Observable在一個特定的排程器上傳送通知給觀察者 (呼叫觀察者的onNext, onCompleted, onError方法),當遇到一個異常時ObserveOn會立即向前傳遞這個onError終止通知,它不會等待慢速消費的Observable接受任何之前它已經收到但還沒有發射的資料項。這可能意味著onError通知會跳到(併吞掉)原始Observable發射的資料項前面。
SubscribeOn操作符的作用類似,但它是用於指定Observable本身在特定的排程器上執行,它同樣會在那個排程器上給觀察者發通知。改操作符只能指定一次,如果指定多次則以第一次為準。而observeOn可以指定多次,每次指定會在observeOn下一句程式碼處生效。
示例程式碼

        stringBuffer = new StringBuffer();
        Observable.create(new Observable.OnSubscribe<Drawable>() {
            @Override
            public void call(Subscriber<? super Drawable> subscriber) {
                //不能執行耗時操作,及更新ui
                stringBuffer.append("\n" + "開始傳送事件" + Thread.currentThread().getName() + "\n");
                Drawable drawable = getResources().getDrawable(R.mipmap.dir);
                subscriber.onNext(drawable);
                subscriber.onCompleted();
            }
        })
                //指定建立Observable在io中
                .subscribeOn(Schedulers.io())
                //由於map中做耗時操作,通過Observable指定發射資料在新的執行緒
                .observeOn(Schedulers.newThread())
                .map(new Func1<Drawable, ImageView>() {
                    @Override
                    public ImageView call(Drawable drawable) {
                        ImageView imageView = new ImageView(getActivity());
                        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
                        imageView.setLayoutParams(params);
                        imageView.setImageDrawable(drawable);

                        return imageView;
                    }
                })
                //操作UI,需要指定在主執行緒
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<ImageView>() {
                    @Override
                    public void call(ImageView imageView) {
                        tv.append(stringBuffer.toString() + "接收資訊事件" + Thread.currentThread().getName());
                        layout.addView(imageView);
                    }
                });複製程式碼

TimeInterval

RxJava 操作符系列五
這裡寫圖片描述

這個操作符通過這張圖能更好的理解,這個操作符將原始Observable轉換為另一個Obserervable,後者發射一個標誌替換前者的資料項,這個標誌表示前者的兩個連續發射物之間流逝的時間長度。新的Observable的第一個發射物表示的是在觀察者訂閱原始Observable到原始Observable發射它的第一項資料之間流逝的時間長度。不存在與原始Observable發射最後一項資料和發射onCompleted通知之間時長對應的發射物。

Observable.interval(1,TimeUnit.SECONDS)
                .filter(new Func1<Long, Boolean>() {
                    @Override
                    public Boolean call(Long aLong) {
                        return aLong<5;
                    }
                })
                .timeInterval()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<TimeInterval<Long>>() {
                    @Override
                    public void onCompleted() {
                        Log.e(TAG, "onCompleted: " );
                    }

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

                    @Override
                    public void onNext(TimeInterval<Long> longTimeInterval) {
                        Log.e(TAG, "onNext: value:"+longTimeInterval.getValue()+"getIntervalInMilliseconds"+longTimeInterval.getIntervalInMilliseconds());
                    }
                });複製程式碼

輸出日誌資訊

onNext: value:0getIntervalInMilliseconds1002
onNext: value:1getIntervalInMilliseconds999
onNext: value:2getIntervalInMilliseconds999
onNext: value:3getIntervalInMilliseconds1000
onNext: value:4getIntervalInMilliseconds1001複製程式碼

通過日誌發現,返回的TimeInterval型別資料,包含時間間隔和值。

#Timestamp
該操作符和TimeInterval一樣最終發射的都是TimeInterval型別資料。但是不同的是,改操作符發射資料每一項包含資料的原始發射時間(TimeInterval是時間間隔)
示例程式碼

Observable.just(1,2,3,4).timestamp().subscribe(new Action1<Timestamped<Integer>>() {
            @Override
            public void call(Timestamped<Integer> integerTimestamped) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
                Log.e(TAG, "value: " + integerTimestamped.getValue() + "       time:   "+sdf.format(new Date(integerTimestamped.getTimestampMillis())) );
            }
        });複製程式碼

輸出日誌資訊

value: 1       time:   2016-12-17-23:33:47
value: 2       time:   2016-12-17-23:33:47
value: 3       time:   2016-12-17-23:33:47
value: 4       time:   2016-12-17-23:33:47複製程式碼

Timeout

如果原始Observable過了指定的一段時間沒有發射任何資料,Timeout操作符會以一個onError通知終止這個Observable。

 Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                try {
                    subscriber.onNext(1);
                    Thread.sleep(100);
                    subscriber.onNext(2);
                    Thread.sleep(200);
                    subscriber.onNext(3);
                    Thread.sleep(300);
                    subscriber.onNext(4);
                    Thread.sleep(400);
                    subscriber.onNext(5);
                    subscriber.onCompleted();
                } catch (InterruptedException e) {
                    subscriber.onError(new Throwable("Error"));
                    e.printStackTrace();
                }
            }
        })
                //此timeout方法預設在computation排程器上執行.
                .timeout(250,TimeUnit.MILLISECONDS)
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        Log.e(TAG, "onCompleted: " );
                    }

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

                    @Override
                    public void onNext(Integer integer) {
                        Log.e(TAG, "onNext: "+integer );
                    }
                });複製程式碼

輸出日誌資訊

onNext: 1
onNext: 2
onNext: 3
onError:複製程式碼

由於傳送資料3後sleep(300)超過設定的時間250ms,則執行onError。timeout還有過載方法可以在超時的時候切換到一個我們指定的備用的Observable,而不是發錯誤通知。它也預設在computation排程器上執行。如下示例程式碼

  Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                try {
                    subscriber.onNext(1);
                    Thread.sleep(100);
                    subscriber.onNext(2);
                    Thread.sleep(200);
                    subscriber.onNext(3);
                    Thread.sleep(300);
                    subscriber.onNext(4);
                    Thread.sleep(400);
                    subscriber.onNext(5);
                    subscriber.onCompleted();
                } catch (InterruptedException e) {
                    subscriber.onError(new Throwable("Error"));
                    e.printStackTrace();
                }
            }
        })
                .timeout(250,TimeUnit.MILLISECONDS,Observable.just(10,11))
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        Log.e(TAG, "onCompleted: " );
                    }

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

                    @Override
                    public void onNext(Integer integer) {
                        Log.e(TAG, "onNext: "+integer );
                    }
                });複製程式碼

輸出日誌資訊

onNext: 1
onNext: 2
onNext: 3
onNext: 10
onNext: 11
onCompleted:複製程式碼

該操作符還有幾個過載方法如 timeout(Func1),timeout(Func1,Observable), timeout(Func0,Func1), timeout(Func0,Func1,Observable)這幾個操作符預設在immediate排程器上執行,具體執行效果可自行觀察程式碼。

To

此係列操作符的作用是將Observable轉換為另一個物件或資料結構。下面介紹幾個常用的to操作符。

toList

發射多項資料的Observable會為每一項資料呼叫onNext方法。你可以用toList操作符改變這個行為,讓Observable將多項資料組合成一個List,然後呼叫一次onNext方法傳遞整個列表,如果原始Observable沒有發射任何資料就呼叫了onCompleted,toList返回的Observable會在呼叫onCompleted之前發射一個空列表。如果原始Observable呼叫了onError,toList返回的Observable會立即呼叫它的觀察者的onError方法。

 Observable.just(1,2,3,4,5).toList().subscribe(new Subscriber<List<Integer>>() {
            @Override
            public void onCompleted() {
                Log.e(TAG, "onCompleted: " );
            }

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

            @Override
            public void onNext(List<Integer> integers) {
                Log.e(TAG, "onNext: "+integers);
            }
        });複製程式碼

如上程式碼,通過toList將單個資料最終以List的形式輸出。

ToMap

該操作符收集原始Observable發射的所有資料項到一個Map(預設是HashMap)然後發射這個Map。我們可以提供一個用於生成Map的Key的函式,還可以提供一個函式轉換資料項到Map儲存的值(預設資料項本身就是值)。
示例程式碼

Observable.just(1,2,3,4)
                .toMap(new Func1<Integer, String>() {
                    @Override
                    public String call(Integer integer) {
                       //生成map的key值
                        return "key"+integer;
                    }
                }).subscribe(new Subscriber<Map<String, Integer>>() {
            @Override
            public void onCompleted() {
                Log.e(TAG, "onCompleted: "  );
            }

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

            @Override
            public void onNext(Map<String, Integer> integerIntegerMap) {
                Log.e(TAG, "onNext: "+integerIntegerMap.toString() );

            }
        });複製程式碼

輸出日誌資訊

onNext: {key4=4, key3=3, key2=2, key1=1}
onCompleted:複製程式碼

該操作符有個兩個引數的構造方法可以更改發射的資料的值,如下

 Observable.just(1,2,3,4)
                .toMap(new Func1<Integer, String>() {
                    @Override
                    public String call(Integer integer) {
                        return "key" + integer;
                    }
                }, new Func1<Integer, Integer>() {
                    @Override
                    public Integer call(Integer integer) {
                        return integer+10;
                    }
                })
                .subscribe(new Subscriber<Map<String, Integer>>() {
            @Override
            public void onCompleted() {
                Log.e(TAG, "onCompleted: "  );
            }

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

            @Override
            public void onNext(Map<String, Integer> integerIntegerMap) {
                Log.e(TAG, "onNext: "+integerIntegerMap.toString() );

            }複製程式碼

輸出日誌資訊

onNext: {key4=14, key3=13, key2=12, key1=11}
onCompleted:複製程式碼

發現此時指定了map的key,並且更改了發射的資料值。

toMutimap

類似於toMap,不同的是,它生成的這個Map同時還是一個ArrayList(預設是這樣,你可以傳遞一個可選的工廠方法修改這個行為)。toMap(Func1)是將原Observable傳送的資料儲存到一個MAP中,並在引數函式中,設定key。但toMultimap操作符在將資料儲存到MAP前,先將資料儲存到Collection,而toMap操作符將資料直接儲存到MAP中,並沒有再包裹一層Collection。

Observable.just(1,2,3,4)
                .toMultimap(new Func1<Integer, String>() {
                    @Override
                    public String call(Integer integer) {
                        return "key"+integer;
                    }
                }).subscribe(new Subscriber<Map<String, Collection<Integer>>>() {
            @Override
            public void onCompleted() {
                Log.e(TAG, "onCompleted: ");
            }

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

            @Override
            public void onNext(Map<String, Collection<Integer>> integerCollectionMap) {
                Log.e(TAG, "onNext: "+integerCollectionMap.toString());
            }
        });複製程式碼

輸出日誌資訊

onNext: {key4=[4], key3=[3], key2=[2], key1=[1]}
onCompleted:複製程式碼

通過上面資訊,也看的兩者區別。

toSortedList

該操作符類似於toList,區別是它可以對資料進行自然排序。如下示例

Integer[] integers = {2, 3, 6, 4, 9,2, 8};
        Observable.from(integers)
                .toSortedList()
                .flatMap(new Func1<List<Integer>, Observable<Integer>>() {
                    @Override
                    public Observable<Integer> call(List<Integer> integer) {
                        Log.e(TAG, "call: "+integer.toString() );
                        return Observable.from(integer);
                    }
                }).subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {
                Log.e(TAG, "onCompleted: " );
            }

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

            @Override
            public void onNext(Integer integer) {
                Log.e(TAG, "onNext: "+integer);
                tv.append("\n" + integer);
            }
        });複製程式碼

輸出日誌資訊

call: [2, 2, 3, 4, 6, 8, 9]
onNext: 2
onNext: 2
onNext: 3
onNext: 4
onNext: 6
onNext: 8
onNext: 9
onCompleted:複製程式碼

今天的這篇文章就到此結束,歡迎大家閱讀,若發現文中有錯誤的地方歡迎留言提出,感謝。

相關文章