[Android開發] RxJava2之路五
阿新 • • 發佈:2019-01-28
一、過濾操作符列表
用於過濾和選擇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: 點選了按鈕");
}
});