1. 程式人生 > >[Android開發] RxJava2之路五

[Android開發] RxJava2之路五

一、過濾操作符列表

用於過濾和選擇Observable發射的資料序列

方法 含義
filter() 過濾資料
takeLast() 只發射最後的N項資料
last() 只發射最後的一項資料
lastOrDefault() 只發射最後的一項資料,如果Observable為空就發射預設值
takeLastBuffer() 將最後的N項資料當做單個數據發射
skip() 跳過開始的N項資料
skipLast() 跳過最後的N項資料
take() 只發射開始的N項資料
first() , takeFirst() 只發射第一項資料,或者滿足某種條件的第一項資料
firstOrDefault() 只發射第一項資料,如果Observable為空就發射預設值
elementAt() 發射第N項資料
elementAtOrDefault() 發射第N項資料,如果Observable資料少於N項就發射預設值
sample() , throttleLast() 定期發射Observable最近的資料
throttleFirst() 定期發射Observable發射的第一項資料
throttleWithTimeout() , debounce() 只有在空閒了一段時間後才發射資料,通俗的說,就是如果一段時間沒有操作,就執行一次操作
timeout() 如果在一個指定的時間段後還沒發射資料,就發射一個異常
distinct() 過濾掉重複資料
distinctUntilChanged() 過濾掉連續重複的資料
ofType() 只發射指定型別的資料
ignoreElements() 丟棄所有的正常資料,只發射錯誤或完成通知

二、過濾操作符

2.1 debounce

被觀察者連續發射的資料的時間間隔 如果在指定時間 就被過濾攔截。

看一個栗子:

    public
void test(){ Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> e) throws Exception { if(e.isDisposed()) return; try { //產生結果的間隔時間分別為100、200、300...1000毫秒 for (int i = 1; i <= 10; i++) { e.onNext(i); //發射資料 Thread.sleep(i * 100); } e.onComplete(); }catch(Exception ex){ e.onError(ex); } } }).subscribeOn(Schedulers.computation()) //被觀察者線上程中執行 .debounce(400,TimeUnit.MILLISECONDS) //如果發射資料間隔少於400就過濾攔截掉 .subscribe(new Consumer<Integer>() { @Override public void accept(Integer integer) throws Exception { Log.e(TAG, "accept: "+integer); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Log.e(TAG, "出錯: "+throwable.toString()); } }); }

輸出是這樣的,400毫秒以前的就被過濾掉了:

02-10 10:24:05.527 31354-8852/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 4
02-10 10:24:05.928 31354-8852/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 5
02-10 10:24:06.438 31354-8852/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 6
02-10 10:24:07.039 31354-8852/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 7
02-10 10:24:07.739 31354-8852/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 8
02-10 10:24:08.540 31354-8852/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 9
02-10 10:24:09.441 31354-8852/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 10

2.2 filter

過濾資料,返回真就是滿足條件,不攔截; 返回假就是不滿足條件,攔截掉,不然觀察者接收到。

這個可以對上面的編輯框輸入搜尋進一步優化,當內容為空的時候就過濾掉。

看一個栗子(上面的修改一下):

    public void test(){
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                if(e.isDisposed()) return;
                try {
                    //產生結果的間隔時間分別為100、200、300...1000毫秒
                    for (int i = 1; i <= 10; i++) {
                        e.onNext(i);
                        Thread.sleep(i * 100);
                    }
                    e.onComplete();
                }catch(Exception ex){
                    e.onError(ex);
                }
            }
        }).subscribeOn(Schedulers.computation())
                .debounce(400,TimeUnit.MILLISECONDS)  //如果發射資料間隔少於400就過濾攔截掉
                .filter(new Predicate<Integer>() {
                    @Override
                    public boolean test(Integer integer) throws Exception {
                        //返回真就是滿足條件,不攔截; 返回假就是不滿足條件,攔截掉,不然觀察者接收到。

                        //大於5的才滿足條件,才不攔截
                        return integer > 5;
                    }
                })
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.e(TAG, "accept: "+integer);
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "出錯: "+throwable.toString());

                    }
                });
    }

對應輸出的結果就是輸出6以及6之後的資料:

02-10 10:26:40.268 10474-11359/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 6
02-10 10:26:40.869 10474-11359/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 7
02-10 10:26:41.569 10474-11359/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 8
02-10 10:26:42.370 10474-11359/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 9
02-10 10:26:43.271 10474-11359/cn.com.minstone.rxjavalearn E/SearchActivity: accept: 10

2.3 take操作符

    public void testLast(){
        Observable.just(1, 2, 3, 4, 5, 6, 7, 8)
                .take(4)   //發射前面四個資料
                .subscribe(new Observer<Integer>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(Integer value) {
                        Log.e(TAG,"收到資料"+value);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

呼叫上面的方法,輸出的是:

02-20 09:05:51.688 28437-28437/? E/FilterActivity: 收到資料1
02-20 09:05:51.688 28437-28437/? E/FilterActivity: 收到資料2
02-20 09:05:51.688 28437-28437/? E/FilterActivity: 收到資料3
02-20 09:05:51.688 28437-28437/? E/FilterActivity: 收到資料4

其他的過濾操作符就自行測試了。

三、過濾的實際例子

3.1 搜尋例子

比如在做搜尋的時候,可以使用debounce減少頻繁的網路請求。避免每輸入(刪除)一個字就做一次網路請求。

不使用Rxbinding的栗子:

        etTest = (EditText) findViewById(R.id.et_test);

        etTest.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(final Editable s) {
                //交給RxJava去做
                if(disposable != null && !disposable.isDisposed()){
                    disposable.dispose();
                }

                disposable = Observable.create(new ObservableOnSubscribe<String>() {
                            @Override
                            public void subscribe(ObservableEmitter<String> e) throws Exception {
                                e.onNext(s.toString());
                            }
                        })
                        .debounce(400, TimeUnit.MILLISECONDS)
                        .subscribeOn(AndroidSchedulers.mainThread())
                        .filter(new Predicate<String>() {
                            @Override
                            public boolean test(String s) throws Exception {
                                 //返回是否滿足條件,真為繼續執行下去,假不滿足條件就是攔截了
                                return s.trim().length() > 0;
                            }
                        })
                        .flatMap(new Function<String, ObservableSource<List<String>>>() {
                            @Override
                            public ObservableSource<List<String>> apply(String string) throws Exception {
                                List<String> list = new ArrayList<String>();
                                for(Character charText:string.toCharArray()){
                                    list.add(charText.toString());
                                }
                                return Observable.just(list);
                            }
                        })
                        .observeOn(Schedulers.io())
                        .subscribe(new Consumer<List<String>>() {
                            @Override
                            public void accept(List<String> strings) throws Exception {
                                Log.e(TAG, "accept: "+strings.size());
                            }
                        });
            }
        });



    }

這樣子當你不停在輸入EditText的內容的時候,是不會列印的啦

使用了Rxbinding的栗子:

    public void testDemo() {
        RxTextView.textChangeEvents(etTest)
                .debounce(300, TimeUnit.MILLISECONDS) //300毫秒內的連續編輯  過濾掉
                .flatMap(new Function<TextViewTextChangeEvent, ObservableSource<String>>() {
                    @Override
                    public ObservableSource<String> apply(@NonNull TextViewTextChangeEvent textViewTextChangeEvent) throws Exception {
                        //把發射的資料來源變為文字String
                        return Observable.just(textViewTextChangeEvent.text().toString());
                    }
                })
                .filter(new Predicate<String>() {
                    @Override
                    public boolean test(@NonNull String s) throws Exception {
                        //是否同意繼續發射
                        return s.length()>0;
                    }
                })
                .subscribe(new Consumer<String>() {
                    @Override
                    public void accept(@NonNull String s) throws Exception {
                        Log.e(TAG, "accept: "+s);
                    }
                });
    }

3.2 防止多次點選

點選一個按鈕,取1秒內的第一次點選響應,防止多次點選。

Rxbinding例子:

這例子使用debounce也可以實現,但是實現的過成不一樣,如果用的是debounce,必須得等待指定的事時間。如果用的是throttleFirst就是不用等待,執行第一個,指定時間的事件都過濾掉。這點要注意。

btTest = (Button) findViewById(R.id.bt_test);

RxView.clicks(btTest)
        .throttleFirst(1, TimeUnit.SECONDS)   //一秒內的點選只拿第一個,他的全過濾掉
        .subscribe(new Action1<Void>() {

            @Override
            public void call(Void aVoid) {
                //onNext回撥
                Log.e(TAG, "call: 點選了按鈕");
            }
        });